2 Copyright (C) 2004-2011 Parallel Realities
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 extern Graphics graphics;
27 memset(keyState, 0, sizeof keyState);
29 memset(joystickState, 0, sizeof joystickState);
33 mouseLeft = mouseRight = 0;
34 waitForButton = false;
39 lastKeyPressed[0] = 0;
51 highlightedWidget = NULL;
57 devNoMonsters = false;
62 char pakPath[PATH_MAX];
63 strlcpy(pakPath, PAKFULLPATH, sizeof(pakPath));
64 if (CFBundleGetMainBundle() != NULL) {
65 CFURLRef pakURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(PAKNAME), NULL, NULL);
68 CFURLGetFileSystemRepresentation(pakURL, true, (UInt8*)pakPath, sizeof(pakPath));
72 pak.setPakFile(pakPath);
74 pak.setPakFile(PAKFULLPATH);
82 memset(lastKeyEvents, ' ', 25);
83 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
84 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
89 void Engine::destroy()
91 debug(("engine: free widgets\n"));
94 debug(("engine: free databuffer\n"));
97 debug(("engine: free binarybuffer\n"));
98 delete[] binaryBuffer;
100 debug(("Clearing Define List...\n"));
104 void Engine::clearCheatVars()
106 memset(lastKeyEvents, ' ', 25);
107 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
108 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
111 bool Engine::compareLastKeyInputs()
113 if (strstr(lastKeyEvents, "lockandload"))
122 void Engine::addKeyEvent()
124 if (strlen(lastKeyPressed) > 1)
131 for (int i = 0 ; i < 25 ; i++)
133 if (lastKeyEvents[i] == ' ')
142 for (int i = 0 ; i < 24 ; i++)
144 lastKeyEvents[i] = lastKeyEvents[i + 1];
150 lastKeyEvents[index] = lastKeyPressed[0];
152 compareLastKeyInputs();
155 void Engine::getInput()
157 SDL_GetMouseState(&mouseX, &mouseY);
159 while (SDL_PollEvent(&event))
170 case SDL_MOUSEBUTTONDOWN:
171 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
172 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
175 case SDL_MOUSEBUTTONUP:
176 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
177 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
184 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
186 lastButtonPressed = -1;
187 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
188 highlightedWidget->redraw();
189 waitForButton = false;
193 if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
195 lastButtonPressed = -2;
196 *highlightedWidget->value = -2;
197 highlightedWidget->redraw();
198 waitForButton = false;
207 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
209 *highlightedWidget->value = -*highlightedWidget->value;
213 *highlightedWidget->value = event.key.keysym.scancode;
216 lastButtonPressed = -1;
217 highlightedWidget->redraw();
218 waitForButton = false;
225 keyState[event.key.keysym.scancode] = 1;
226 strlcpy(lastKeyPressed, SDL_GetKeyName(event.key.keysym.scancode), sizeof lastKeyPressed);
231 keyState[event.key.keysym.scancode] = 0;
234 case SDL_JOYAXISMOTION:
235 if (event.jaxis.axis == 0)
237 joyX = event.jaxis.value;
239 else if (event.jaxis.axis == 1)
241 joyY = event.jaxis.value;
246 case SDL_JOYBUTTONDOWN:
250 lastButtonPressed = event.jbutton.button;
251 *highlightedWidget->value = lastButtonPressed;
252 highlightedWidget->redraw();
253 waitForButton = false;
258 joystickState[event.jbutton.button] = 1;
261 case SDL_JOYBUTTONUP:
262 joystickState[event.jbutton.button] = 0;
265 case SDL_WINDOWEVENT:
266 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
276 int Engine::getMouseX() const
281 int Engine::getMouseY() const
286 void Engine::setMouse(int x, int y)
288 SDL_WarpMouseInWindow(graphics.window, x, y);
291 bool Engine::userAccepts()
293 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]))
301 void Engine::flushInput()
303 while (SDL_PollEvent(&event)){}
306 void Engine::clearInput()
308 memset(keyState, 0, sizeof keyState);
310 mouseLeft = mouseRight = 0;
313 void Engine::setUserHome(const char *path)
315 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
316 debug(("User Home = %s\n", path));
319 Pak *Engine::getPak()
325 Searches the pak file for the required data. When
326 it is found, the data is read into a character buffer.
327 In the case of music, the data music be written to a temporary directory
328 since SDL currently provides no means to load music directly from memory
330 bool Engine::unpack(const char *filename, int fileType)
334 if (fileType == PAK_DATA)
341 delete[] binaryBuffer;
345 if (fileType != PAK_DATA)
347 if (!pak.unpack(filename, &binaryBuffer))
354 if (!pak.unpack(filename, &dataBuffer))
360 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
362 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
365 printf("Fatal Error: SDL_RWops allocation failed\n");
370 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
372 char tempPath[PATH_MAX];
376 if (fileType == PAK_MUSIC)
378 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
379 fp = fopen(tempPath, "wb");
382 if (fileType == PAK_TAGS)
384 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
385 fp = fopen(tempPath, "wb");
388 if (fileType == PAK_FONT)
390 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
391 fp = fopen(tempPath, "wb");
396 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
400 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
402 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
408 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
413 bool Engine::loadData(const char *filename)
421 return unpack(filename, PAK_DATA);
425 fp = fopen(filename, "rb");
429 fseek(fp, 0, SEEK_END);
431 int fSize = ftell(fp);
435 dataBuffer = new unsigned char[fSize + 1];
437 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
440 dataBuffer[fSize] = 0;
444 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
449 void Engine::reportFontFailure()
451 printf("\nUnable to load font. The game cannot continue without it.\n");
452 printf("Please confirm that the game and all required SDL libraries are installed\n");
453 printf("The following information may be useful to you,\n\n");
454 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
455 printf("Location of TMP directory: %s\n", userHomeDirectory);
456 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
460 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
462 playerPosX = x - OFFSETX;
463 playerPosY = y - OFFSETY;
465 Math::limitInt(&playerPosX, limitLeft, limitRight);
466 Math::limitInt(&playerPosY, limitUp, limitDown);
469 int Engine::getFrameLoop() const
474 void Engine::doFrameLoop()
476 Math::wrapChar(&(++frameLoop), 0, 59);
479 void Engine::doTimeDifference()
481 timeDifference = (time2 - time1) / 10.0;
483 time2 = SDL_GetTicks();
486 float Engine::getTimeDifference() const
488 return timeDifference;
491 void Engine::resetTimeDifference()
493 time1 = time2 = SDL_GetTicks();
496 void Engine::setInfoMessage(const char *message, int priority, int type)
498 if (priority >= messagePriority)
500 strlcpy(this->message, message, sizeof this->message);
502 messagePriority = priority;
507 void Engine::deleteWidgets()
511 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
516 highlightedWidget = NULL;
519 void Engine::addWidget(Widget *widget)
521 widget->previous = (Widget*)widgetList.getTail();
522 widgetList.add(widget);
525 bool Engine::loadWidgets(const char *filename)
529 if (!loadData(filename))
532 char token[50], name[50], groupName[50], label[80], options[100], *line;
539 line = strtok((char*)dataBuffer, "\n");
543 sscanf(line, "%s", token);
545 if (strcmp(token, "END") == 0)
548 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
556 if (strcmp(token, widgetName[i]) == 0)
559 if (strcmp("-1", widgetName[i]) == 0)
565 widget->setProperties(name, groupName, label, options, x, y, min, max);
570 if ((line = strtok(NULL, "\n")) == NULL)
574 highlightedWidget = (Widget*)widgetList.getHead()->next;
579 Widget *Engine::getWidgetByName(const char *name)
581 Widget *widget = (Widget*)widgetList.getHead();
583 while (widget->next != NULL)
585 widget = (Widget*)widget->next;
587 if (strcmp(widget->name, name) == 0)
591 debug(("No such widget '%s'\n", name));
596 void Engine::showWidgetGroup(const char *groupName, bool show)
600 Widget *widget = (Widget*)widgetList.getHead();
602 while (widget->next != NULL)
604 widget = (Widget*)widget->next;
606 if (strcmp(widget->groupName, groupName) == 0)
608 widget->visible = show;
615 debug(("Group '%s' does not exist\n", groupName));
618 void Engine::enableWidgetGroup(const char *groupName, bool show)
622 Widget *widget = (Widget*)widgetList.getHead();
624 while (widget->next != NULL)
626 widget = (Widget*)widget->next;
628 if (strcmp(widget->groupName, groupName) == 0)
630 widget->enabled = show;
637 debug(("Group '%s' does not exist\n", groupName));
640 void Engine::showWidget(const char *name, bool show)
642 Widget *widget = getWidgetByName(name);
645 widget->visible = show;
650 void Engine::enableWidget(const char *name, bool enable)
652 Widget *widget = getWidgetByName(name);
655 widget->enabled = enable;
660 void Engine::setWidgetVariable(const char *name, int *variable)
662 Widget *widget = getWidgetByName(name);
664 widget->value = variable;
667 bool Engine::widgetChanged(const char *name)
669 Widget *widget = getWidgetByName(name);
671 return widget->changed;
676 void Engine::highlightWidget(int dir)
678 highlightedWidget->redraw();
684 if (highlightedWidget->next != NULL)
686 highlightedWidget = (Widget*)highlightedWidget->next;
690 highlightedWidget = (Widget*)widgetList.getHead()->next;
693 if (highlightedWidget->type == 4)
696 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
705 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
707 highlightedWidget = highlightedWidget->previous;
711 highlightedWidget = (Widget*)widgetList.getTail();
714 if (highlightedWidget->type == WG_LABEL)
717 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
722 highlightedWidget->redraw();
725 void Engine::highlightWidget(const char *name)
727 highlightedWidget = getWidgetByName(name);
730 int Engine::processWidgets()
734 if (keyState[SDL_SCANCODE_UP])
741 if (keyState[SDL_SCANCODE_DOWN])
748 if (keyState[SDL_SCANCODE_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
752 if (*highlightedWidget->value > highlightedWidget->min)
754 *highlightedWidget->value = *highlightedWidget->value - 1;
756 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
758 highlightedWidget->changed = true;
761 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
765 if (keyState[SDL_SCANCODE_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
769 if (*highlightedWidget->value < highlightedWidget->max)
771 *highlightedWidget->value = *highlightedWidget->value + 1;
773 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
775 highlightedWidget->changed = true;
778 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
782 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]))
784 if (highlightedWidget->value == NULL)
786 debug(("%s has not been implemented!\n", highlightedWidget->name));
790 if (highlightedWidget->type == WG_BUTTON)
792 *highlightedWidget->value = 1;
793 highlightedWidget->changed = true;
795 else if (highlightedWidget->type == WG_JOYPAD)
797 waitForButton = true;
801 if (*highlightedWidget->value > -1000)
803 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
806 else if (highlightedWidget->type == WG_KEYBOARD)
809 waitForButton = false;
811 *highlightedWidget->value = -*highlightedWidget->value;
826 char *strtok_r(char *s1, const char *s2, char **lasts)
835 while (*s1 && strchr(s2, *s1))
847 while(*s1 && !strchr(s2, *s1))
864 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
865 game at compile time and run time, so everything is syncronised. This technique has the advantage of
866 allowing the game's data to be human readable and easy to maintain.
868 bool Engine::loadDefines()
870 char string[2][1024];
872 if (!loadData("data/defines.h"))
875 char *token = strtok((char*)dataBuffer, "\n");
881 token = strtok(NULL, "\n");
885 if (!strstr(token, "/*"))
887 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
889 data->set(string[0], string[1], 1, 1);
890 defineList.add(data);
898 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
899 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
900 1) It makes the game data human readable and 2) It means if I change a #define in
901 the code, I don't have to change all the data entries too. You probably already
902 thought of that though... :)
904 int Engine::getValueOfDefine(const char *word)
908 Data *data = (Data*)defineList.getHead();
910 while (data->next != NULL)
912 data = (Data*)data->next;
914 if (strcmp(data->key, word) == 0)
916 rtn = atoi(data->value);
921 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
926 Does the opposite of the above(!)
928 char *Engine::getDefineOfValue(const char *prefix, int value)
932 Data *data = (Data*)defineList.getHead();
934 while (data->next != NULL)
936 data = (Data*)data->next;
938 if (strstr(data->key, prefix))
940 rtn = atoi(data->value);
949 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
954 I like this function. It receives a list of flags declared by their #define name... like
955 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
956 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
957 the values together and then returns them... phew! Makes data files human readable though :)
959 int Engine::getValueOfFlagTokens(const char *realLine)
961 if (strcmp(realLine, "0") == 0)
968 strlcpy(line, realLine, sizeof line);
972 char *word = strtok_r(line, "+", &store);
976 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
984 data = (Data*)defineList.getHead();
987 while (data->next != NULL)
989 data = (Data*)data->next;
991 if (strcmp(data->key, word) == 0)
994 sscanf(data->value, "%d", &value);
998 sscanf(data->value, "%*s %*d %*s %d", &value);
1010 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1011 #if IGNORE_FLAGTOKEN_ERRORS
1018 word = strtok_r(NULL, "+", &store);
1026 void Engine::delay(unsigned int frameLimit) {
1027 unsigned int ticks = SDL_GetTicks();
1029 if(frameLimit < ticks)
1032 if(frameLimit > ticks + 16)
1035 SDL_Delay(frameLimit - ticks);