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(SDL_GetKeyFromScancode(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");
396 else if (fileType == PAK_TAGS)
398 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
399 fp = fopen(tempPath, "wb");
401 else if (fileType == PAK_FONT)
403 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
404 fp = fopen(tempPath, "wb");
409 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
413 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
415 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
421 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
426 bool Engine::loadData(const char *filename)
434 return unpack(filename, PAK_DATA);
438 fp = fopen(filename, "rb");
442 fseek(fp, 0, SEEK_END);
444 int fSize = ftell(fp);
448 dataBuffer = new unsigned char[fSize + 1];
450 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
453 dataBuffer[fSize] = 0;
457 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
462 void Engine::reportFontFailure()
464 printf("\nUnable to load font. The game cannot continue without it.\n");
465 printf("Please confirm that the game and all required SDL libraries are installed\n");
466 printf("The following information may be useful to you,\n\n");
467 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
468 printf("Location of TMP directory: %s\n", userHomeDirectory);
469 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
473 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
475 playerPosX = x - OFFSETX;
476 playerPosY = y - OFFSETY;
478 Math::limitInt(&playerPosX, limitLeft, limitRight);
479 Math::limitInt(&playerPosY, limitUp, limitDown);
482 int Engine::getFrameLoop() const
487 void Engine::doFrameLoop()
489 Math::wrapChar(&(++frameLoop), 0, 59);
492 void Engine::doTimeDifference()
494 timeDifference = (time2 - time1) / 10.0;
496 time2 = SDL_GetTicks();
499 float Engine::getTimeDifference() const
501 return timeDifference;
504 void Engine::resetTimeDifference()
506 time1 = time2 = SDL_GetTicks();
509 void Engine::setInfoMessage(const char *message, int priority, int type)
511 if (priority >= messagePriority)
513 strlcpy(this->message, message, sizeof this->message);
515 messagePriority = priority;
520 void Engine::deleteWidgets()
524 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
529 highlightedWidget = NULL;
532 void Engine::addWidget(Widget *widget)
534 widget->previous = (Widget*)widgetList.getTail();
535 widgetList.add(widget);
538 bool Engine::loadWidgets(const char *filename)
542 if (!loadData(filename))
545 char token[50], name[50], groupName[50], label[80], options[100], *line;
552 line = strtok((char*)dataBuffer, "\n");
556 sscanf(line, "%s", token);
558 if (strcmp(token, "END") == 0)
561 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
569 if (strcmp(token, widgetName[i]) == 0)
572 if (strcmp("-1", widgetName[i]) == 0)
578 widget->setProperties(name, groupName, label, options, x, y, min, max);
583 if ((line = strtok(NULL, "\n")) == NULL)
587 highlightedWidget = (Widget*)widgetList.getHead()->next;
592 Widget *Engine::getWidgetByName(const char *name)
594 Widget *widget = (Widget*)widgetList.getHead();
596 while (widget->next != NULL)
598 widget = (Widget*)widget->next;
600 if (strcmp(widget->name, name) == 0)
604 debug(("No such widget '%s'\n", name));
609 void Engine::showWidgetGroup(const char *groupName, bool show)
613 Widget *widget = (Widget*)widgetList.getHead();
615 while (widget->next != NULL)
617 widget = (Widget*)widget->next;
619 if (strcmp(widget->groupName, groupName) == 0)
621 widget->visible = show;
628 debug(("Group '%s' does not exist\n", groupName));
631 void Engine::enableWidgetGroup(const char *groupName, bool show)
635 Widget *widget = (Widget*)widgetList.getHead();
637 while (widget->next != NULL)
639 widget = (Widget*)widget->next;
641 if (strcmp(widget->groupName, groupName) == 0)
643 widget->enabled = show;
650 debug(("Group '%s' does not exist\n", groupName));
653 void Engine::showWidget(const char *name, bool show)
655 Widget *widget = getWidgetByName(name);
658 widget->visible = show;
663 void Engine::enableWidget(const char *name, bool enable)
665 Widget *widget = getWidgetByName(name);
668 widget->enabled = enable;
673 void Engine::setWidgetVariable(const char *name, int *variable)
675 Widget *widget = getWidgetByName(name);
677 widget->value = variable;
680 bool Engine::widgetChanged(const char *name)
682 Widget *widget = getWidgetByName(name);
684 return widget->changed;
689 void Engine::highlightWidget(int dir)
691 highlightedWidget->redraw();
697 if (highlightedWidget->next != NULL)
699 highlightedWidget = (Widget*)highlightedWidget->next;
703 highlightedWidget = (Widget*)widgetList.getHead()->next;
706 if (highlightedWidget->type == 4)
709 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
718 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
720 highlightedWidget = highlightedWidget->previous;
724 highlightedWidget = (Widget*)widgetList.getTail();
727 if (highlightedWidget->type == WG_LABEL)
730 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
735 highlightedWidget->redraw();
738 void Engine::highlightWidget(const char *name)
740 highlightedWidget = getWidgetByName(name);
743 int Engine::processWidgets()
747 if (keyState[SDL_SCANCODE_UP] || joykeyY < 0)
754 if (keyState[SDL_SCANCODE_DOWN] || joykeyY > 0)
761 if ((keyState[SDL_SCANCODE_LEFT] || joykeyX < 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
765 if (*highlightedWidget->value > highlightedWidget->min)
767 *highlightedWidget->value = *highlightedWidget->value - 1;
769 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
771 highlightedWidget->changed = true;
774 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
778 if ((keyState[SDL_SCANCODE_RIGHT] || joykeyX > 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
782 if (*highlightedWidget->value < highlightedWidget->max)
784 *highlightedWidget->value = *highlightedWidget->value + 1;
786 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
788 highlightedWidget->changed = true;
791 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
795 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]) || (joykeyFire && highlightedWidget->type != WG_JOYPAD))
797 if (highlightedWidget->value == NULL)
799 debug(("%s has not been implemented!\n", highlightedWidget->name));
803 if (highlightedWidget->type == WG_BUTTON)
805 *highlightedWidget->value = 1;
806 highlightedWidget->changed = true;
808 else if (highlightedWidget->type == WG_JOYPAD)
810 waitForButton = true;
814 if (*highlightedWidget->value > -1000)
816 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
819 else if (highlightedWidget->type == WG_KEYBOARD)
822 waitForButton = false;
824 *highlightedWidget->value = -*highlightedWidget->value;
835 if (joykeyX > 0 && highlightedWidget->type == WG_JOYPAD)
837 waitForButton = true;
841 if (*highlightedWidget->value > -1000)
843 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
849 if (joykeyX < 0 && highlightedWidget->type == WG_JOYPAD)
853 lastButtonPressed = -1;
854 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
858 lastButtonPressed = -2;
859 *highlightedWidget->value = -2;
862 waitForButton = false;
864 highlightedWidget->redraw();
872 char *strtok_r(char *s1, const char *s2, char **lasts)
881 while (*s1 && strchr(s2, *s1))
893 while(*s1 && !strchr(s2, *s1))
910 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
911 game at compile time and run time, so everything is syncronised. This technique has the advantage of
912 allowing the game's data to be human readable and easy to maintain.
914 bool Engine::loadDefines()
916 char string[2][1024];
918 if (!loadData("data/defines.h"))
921 strtok((char*)dataBuffer, "\n");
925 char *token = strtok(NULL, "\n");
929 if (!strstr(token, "/*"))
931 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
932 Data *data = new Data();
933 data->set(string[0], string[1], 1, 1);
934 defineList.add(data);
942 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
943 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
944 1) It makes the game data human readable and 2) It means if I change a #define in
945 the code, I don't have to change all the data entries too. You probably already
946 thought of that though... :)
948 int Engine::getValueOfDefine(const char *word)
952 Data *data = (Data*)defineList.getHead();
954 while (data->next != NULL)
956 data = (Data*)data->next;
958 if (strcmp(data->key, word) == 0)
960 rtn = atoi(data->value);
965 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
970 Does the opposite of the above(!)
972 char *Engine::getDefineOfValue(const char *prefix, int value)
976 Data *data = (Data*)defineList.getHead();
978 while (data->next != NULL)
980 data = (Data*)data->next;
982 if (strstr(data->key, prefix))
984 rtn = atoi(data->value);
993 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
998 I like this function. It receives a list of flags declared by their #define name... like
999 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
1000 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
1001 the values together and then returns them... phew! Makes data files human readable though :)
1003 int Engine::getValueOfFlagTokens(const char *realLine)
1005 if (strcmp(realLine, "0") == 0)
1012 strlcpy(line, realLine, sizeof line);
1016 char *word = strtok_r(line, "+", &store);
1020 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
1028 data = (Data*)defineList.getHead();
1031 while (data->next != NULL)
1033 data = (Data*)data->next;
1035 if (strcmp(data->key, word) == 0)
1038 sscanf(data->value, "%d", &value);
1042 sscanf(data->value, "%*s %*d %*s %d", &value);
1054 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1055 #if IGNORE_FLAGTOKEN_ERRORS
1062 word = strtok_r(NULL, "+", &store);
1070 void Engine::delay(unsigned int frameLimit) {
1071 unsigned int ticks = SDL_GetTicks();
1073 if(frameLimit < ticks)
1076 if(frameLimit > ticks + 16)
1079 SDL_Delay(frameLimit - ticks);