2 Copyright (C) 2004-2010 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.
26 for (int i = 0 ; i < 350 ; i++)
31 for (int i = 0 ; i < 32 ; i++)
38 mouseLeft = mouseRight = 0;
39 waitForButton = false;
44 lastKeyPressed[0] = 0;
56 highlightedWidget = NULL;
62 devNoMonsters = false;
67 char pakPath[PATH_MAX];
68 strlcpy(pakPath, PAKFULLPATH, sizeof(pakPath));
69 if (CFBundleGetMainBundle() != NULL) {
70 CFURLRef pakURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(PAKNAME), NULL, NULL);
73 CFURLGetFileSystemRepresentation(pakURL, true, (UInt8*)pakPath, sizeof(pakPath));
77 pak.setPakFile(pakPath);
79 pak.setPakFile(PAKFULLPATH);
87 memset(lastKeyEvents, ' ', 25);
88 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
89 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
94 void Engine::destroy()
96 debug(("engine: free widgets\n"));
99 debug(("engine: free databuffer\n"));
102 debug(("engine: free binarybuffer\n"));
103 delete[] binaryBuffer;
105 debug(("Clearing Define List...\n"));
109 void Engine::clearCheatVars()
111 memset(lastKeyEvents, ' ', 25);
112 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
113 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
116 bool Engine::compareLastKeyInputs()
118 if (strstr(lastKeyEvents, "lockandload"))
127 void Engine::addKeyEvent()
129 if (strlen(lastKeyPressed) > 1)
136 for (int i = 0 ; i < 25 ; i++)
138 if (lastKeyEvents[i] == ' ')
147 for (int i = 0 ; i < 24 ; i++)
149 lastKeyEvents[i] = lastKeyEvents[i + 1];
155 lastKeyEvents[index] = lastKeyPressed[0];
157 compareLastKeyInputs();
160 void Engine::getInput()
162 SDL_GetMouseState(&mouseX, &mouseY);
164 while (SDL_PollEvent(&event))
175 case SDL_MOUSEBUTTONDOWN:
176 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
177 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
180 case SDL_MOUSEBUTTONUP:
181 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
182 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
189 if (event.key.keysym.sym == SDLK_ESCAPE)
191 lastButtonPressed = -1;
192 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
193 highlightedWidget->redraw();
194 waitForButton = false;
198 if (event.key.keysym.sym == SDLK_BACKSPACE)
200 lastButtonPressed = -2;
201 *highlightedWidget->value = -2;
202 highlightedWidget->redraw();
203 waitForButton = false;
212 if (event.key.keysym.sym == SDLK_ESCAPE)
214 *highlightedWidget->value = -*highlightedWidget->value;
218 *highlightedWidget->value = event.key.keysym.sym;
221 lastButtonPressed = -1;
222 highlightedWidget->redraw();
223 waitForButton = false;
230 keyState[event.key.keysym.sym] = 1;
231 strlcpy(lastKeyPressed, SDL_GetKeyName(event.key.keysym.sym), sizeof lastKeyPressed);
236 keyState[event.key.keysym.sym] = 0;
239 case SDL_JOYAXISMOTION:
240 if (event.jaxis.axis == 0)
242 joyX = event.jaxis.value;
244 else if (event.jaxis.axis == 1)
246 joyY = event.jaxis.value;
251 case SDL_JOYBUTTONDOWN:
255 lastButtonPressed = event.jbutton.button;
256 *highlightedWidget->value = lastButtonPressed;
257 highlightedWidget->redraw();
258 waitForButton = false;
263 joystickState[event.jbutton.button] = 1;
266 case SDL_JOYBUTTONUP:
267 joystickState[event.jbutton.button] = 0;
276 int Engine::getMouseX() const
281 int Engine::getMouseY() const
286 void Engine::setMouse(int x, int y)
291 bool Engine::userAccepts()
293 if ((keyState[SDLK_SPACE]) || (keyState[SDLK_ESCAPE]) || (keyState[SDLK_LCTRL]) || (keyState[SDLK_RCTRL]) || (keyState[SDLK_RETURN]) || (keyState[SDLK_LCTRL]))
301 void Engine::flushInput()
303 while (SDL_PollEvent(&event)){}
306 void Engine::clearInput()
308 for (int i = 0 ; i < 350 ; i++)
311 mouseLeft = mouseRight = 0;
314 void Engine::setUserHome(const char *path)
316 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
317 debug(("User Home = %s\n", path));
320 Pak *Engine::getPak()
326 Searches the pak file for the required data. When
327 it is found, the data is read into a character buffer.
328 In the case of music, the data music be written to a temporary directory
329 since SDL currently provides no means to load music directly from memory
331 bool Engine::unpack(const char *filename, int fileType)
335 if (fileType == PAK_DATA)
342 delete[] binaryBuffer;
346 if (fileType != PAK_DATA)
348 if (!pak.unpack(filename, &binaryBuffer))
355 if (!pak.unpack(filename, &dataBuffer))
361 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
363 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
366 printf("Fatal Error: SDL_RWops allocation failed\n");
371 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
373 char tempPath[PATH_MAX];
377 if (fileType == PAK_MUSIC)
379 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
380 fp = fopen(tempPath, "wb");
383 if (fileType == PAK_TAGS)
385 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
386 fp = fopen(tempPath, "wb");
389 if (fileType == PAK_FONT)
391 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
392 fp = fopen(tempPath, "wb");
397 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
401 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
403 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
409 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
414 bool Engine::loadData(const char *filename)
422 return unpack(filename, PAK_DATA);
426 fp = fopen(filename, "rb");
430 fseek(fp, 0, SEEK_END);
432 int fSize = ftell(fp);
436 dataBuffer = new unsigned char[fSize + 1];
438 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
441 dataBuffer[fSize] = 0;
445 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
450 void Engine::reportFontFailure()
452 printf("\nUnable to load font. The game cannot continue without it.\n");
453 printf("Please confirm that the game and all required SDL libraries are installed\n");
454 printf("The following information may be useful to you,\n\n");
455 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
456 printf("Location of TMP directory: %s\n", userHomeDirectory);
457 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
461 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
463 playerPosX = x - OFFSETX;
464 playerPosY = y - OFFSETY;
466 Math::limitInt(&playerPosX, limitLeft, limitRight);
467 Math::limitInt(&playerPosY, limitUp, limitDown);
470 int Engine::getFrameLoop() const
475 void Engine::doFrameLoop()
477 Math::wrapChar(&(++frameLoop), 0, 59);
480 void Engine::doTimeDifference()
482 timeDifference = (time2 - time1) / 10.0;
484 time2 = SDL_GetTicks();
487 float Engine::getTimeDifference() const
489 return timeDifference;
492 void Engine::resetTimeDifference()
494 time1 = time2 = SDL_GetTicks();
497 void Engine::setInfoMessage(const char *message, int priority, int type)
499 if (priority >= messagePriority)
501 strlcpy(this->message, message, sizeof this->message);
503 messagePriority = priority;
508 void Engine::deleteWidgets()
512 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
517 highlightedWidget = NULL;
520 void Engine::addWidget(Widget *widget)
522 widget->previous = (Widget*)widgetList.getTail();
523 widgetList.add(widget);
526 bool Engine::loadWidgets(const char *filename)
530 if (!loadData(filename))
533 char token[50], name[50], groupName[50], label[80], options[100], *line;
540 line = strtok((char*)dataBuffer, "\n");
544 sscanf(line, "%s", token);
546 if (strcmp(token, "END") == 0)
549 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
557 if (strcmp(token, widgetName[i]) == 0)
560 if (strcmp("-1", widgetName[i]) == 0)
566 widget->setProperties(name, groupName, label, options, x, y, min, max);
571 if ((line = strtok(NULL, "\n")) == NULL)
575 highlightedWidget = (Widget*)widgetList.getHead()->next;
580 Widget *Engine::getWidgetByName(const char *name)
582 Widget *widget = (Widget*)widgetList.getHead();
584 while (widget->next != NULL)
586 widget = (Widget*)widget->next;
588 if (strcmp(widget->name, name) == 0)
592 debug(("No such widget '%s'\n", name));
597 void Engine::showWidgetGroup(const char *groupName, bool show)
601 Widget *widget = (Widget*)widgetList.getHead();
603 while (widget->next != NULL)
605 widget = (Widget*)widget->next;
607 if (strcmp(widget->groupName, groupName) == 0)
609 widget->visible = show;
616 debug(("Group '%s' does not exist\n", groupName));
619 void Engine::enableWidgetGroup(const char *groupName, bool show)
623 Widget *widget = (Widget*)widgetList.getHead();
625 while (widget->next != NULL)
627 widget = (Widget*)widget->next;
629 if (strcmp(widget->groupName, groupName) == 0)
631 widget->enabled = show;
638 debug(("Group '%s' does not exist\n", groupName));
641 void Engine::showWidget(const char *name, bool show)
643 Widget *widget = getWidgetByName(name);
646 widget->visible = show;
651 void Engine::enableWidget(const char *name, bool enable)
653 Widget *widget = getWidgetByName(name);
656 widget->enabled = enable;
661 void Engine::setWidgetVariable(const char *name, int *variable)
663 Widget *widget = getWidgetByName(name);
665 widget->value = variable;
668 bool Engine::widgetChanged(const char *name)
670 Widget *widget = getWidgetByName(name);
672 return widget->changed;
677 void Engine::highlightWidget(int dir)
679 highlightedWidget->redraw();
685 if (highlightedWidget->next != NULL)
687 highlightedWidget = (Widget*)highlightedWidget->next;
691 highlightedWidget = (Widget*)widgetList.getHead()->next;
694 if (highlightedWidget->type == 4)
697 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
706 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
708 highlightedWidget = highlightedWidget->previous;
712 highlightedWidget = (Widget*)widgetList.getTail();
715 if (highlightedWidget->type == WG_LABEL)
718 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
723 highlightedWidget->redraw();
726 void Engine::highlightWidget(const char *name)
728 highlightedWidget = getWidgetByName(name);
731 int Engine::processWidgets()
735 if (keyState[SDLK_UP])
742 if (keyState[SDLK_DOWN])
749 if (keyState[SDLK_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
753 if (*highlightedWidget->value > highlightedWidget->min)
755 *highlightedWidget->value = *highlightedWidget->value - 1;
757 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
759 highlightedWidget->changed = true;
762 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
766 if (keyState[SDLK_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
770 if (*highlightedWidget->value < highlightedWidget->max)
772 *highlightedWidget->value = *highlightedWidget->value + 1;
774 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
776 highlightedWidget->changed = true;
779 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
783 if ((keyState[SDLK_RETURN]) || (keyState[SDLK_SPACE]) || (keyState[SDLK_LCTRL]))
785 if (highlightedWidget->value == NULL)
787 debug(("%s has not been implemented!\n", highlightedWidget->name));
791 if (highlightedWidget->type == WG_BUTTON)
793 *highlightedWidget->value = 1;
794 highlightedWidget->changed = true;
796 else if (highlightedWidget->type == WG_JOYPAD)
798 waitForButton = true;
802 if (*highlightedWidget->value > -1000)
804 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
807 else if (highlightedWidget->type == WG_KEYBOARD)
810 waitForButton = false;
812 *highlightedWidget->value = -*highlightedWidget->value;
827 char *strtok_r(char *s1, const char *s2, char **lasts)
836 while (*s1 && strchr(s2, *s1))
848 while(*s1 && !strchr(s2, *s1))
865 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
866 game at compile time and run time, so everything is syncronised. This technique has the advantage of
867 allowing the game's data to be human readable and easy to maintain.
869 bool Engine::loadDefines()
871 char string[2][1024];
873 if (!loadData("data/defines.h"))
876 char *token = strtok((char*)dataBuffer, "\n");
882 token = strtok(NULL, "\n");
886 if (!strstr(token, "/*"))
888 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
890 data->set(string[0], string[1], 1, 1);
891 defineList.add(data);
899 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
900 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
901 1) It makes the game data human readable and 2) It means if I change a #define in
902 the code, I don't have to change all the data entries too. You probably already
903 thought of that though... :)
905 int Engine::getValueOfDefine(const char *word)
909 Data *data = (Data*)defineList.getHead();
911 while (data->next != NULL)
913 data = (Data*)data->next;
915 if (strcmp(data->key, word) == 0)
917 rtn = atoi(data->value);
922 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
927 Does the opposite of the above(!)
929 char *Engine::getDefineOfValue(const char *prefix, int value)
933 Data *data = (Data*)defineList.getHead();
935 while (data->next != NULL)
937 data = (Data*)data->next;
939 if (strstr(data->key, prefix))
941 rtn = atoi(data->value);
950 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
955 I like this function. It receives a list of flags declared by their #define name... like
956 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
957 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
958 the values together and then returns them... phew! Makes data files human readable though :)
960 int Engine::getValueOfFlagTokens(const char *realLine)
962 if (strcmp(realLine, "0") == 0)
969 strlcpy(line, realLine, sizeof line);
973 char *word = strtok_r(line, "+", &store);
977 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
985 data = (Data*)defineList.getHead();
988 while (data->next != NULL)
990 data = (Data*)data->next;
992 if (strcmp(data->key, word) == 0)
995 sscanf(data->value, "%d", &value);
999 sscanf(data->value, "%*s %*d %*s %d", &value);
1011 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1012 #if IGNORE_FLAGTOKEN_ERRORS
1019 word = strtok_r(NULL, "+", &store);
1027 void Engine::delay(unsigned int frameLimit) {
1028 unsigned int ticks = SDL_GetTicks();
1030 if(frameLimit < ticks)
1033 if(frameLimit > ticks + 16)
1036 SDL_Delay(frameLimit - ticks);