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)
333 if (fileType == PAK_DATA)
340 delete[] binaryBuffer;
344 if (fileType != PAK_DATA)
346 if (!pak.unpack(filename, &binaryBuffer))
353 if (!pak.unpack(filename, &dataBuffer))
359 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
361 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
364 printf("Fatal Error: SDL_RWops allocation failed\n");
369 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
371 char tempPath[PATH_MAX];
375 if (fileType == PAK_MUSIC)
377 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
378 fp = fopen(tempPath, "wb");
381 if (fileType == PAK_TAGS)
383 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
384 fp = fopen(tempPath, "wb");
387 if (fileType == PAK_FONT)
389 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
390 fp = fopen(tempPath, "wb");
395 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
399 fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp);
403 debug(("unpack() : Loaded %s (%d)\n", filename, pak.getUncompressedSize()));
408 bool Engine::loadData(const char *filename)
414 return unpack(filename, PAK_DATA);
418 fp = fopen(filename, "rb");
422 fseek(fp, 0, SEEK_END);
424 int fSize = ftell(fp);
428 dataBuffer = new unsigned char[fSize + 1];
430 fread(dataBuffer, 1, fSize, fp);
431 dataBuffer[fSize] = 0;
435 debug(("loadData() : Loaded %s (%d)\n", filename, fSize));
440 void Engine::reportFontFailure()
442 printf("\nUnable to load font. The game cannot continue without it.\n");
443 printf("Please confirm that the game and all required SDL libraries are installed\n");
444 printf("The following information may be useful to you,\n\n");
445 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
446 printf("Location of TMP directory: %s\n", userHomeDirectory);
447 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
451 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
453 playerPosX = x - OFFSETX;
454 playerPosY = y - OFFSETY;
456 Math::limitInt(&playerPosX, limitLeft, limitRight);
457 Math::limitInt(&playerPosY, limitUp, limitDown);
460 int Engine::getFrameLoop() const
465 void Engine::doFrameLoop()
467 Math::wrapChar(&(++frameLoop), 0, 59);
470 void Engine::doTimeDifference()
472 timeDifference = (time2 - time1) / 10.0;
474 time2 = SDL_GetTicks();
477 float Engine::getTimeDifference() const
479 return timeDifference;
482 void Engine::resetTimeDifference()
484 time1 = time2 = SDL_GetTicks();
487 void Engine::setInfoMessage(const char *message, int priority, int type)
489 if (priority >= messagePriority)
491 strlcpy(this->message, message, sizeof this->message);
493 messagePriority = priority;
498 void Engine::deleteWidgets()
502 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
507 highlightedWidget = NULL;
510 void Engine::addWidget(Widget *widget)
512 widget->previous = (Widget*)widgetList.getTail();
513 widgetList.add(widget);
516 bool Engine::loadWidgets(const char *filename)
520 if (!loadData(filename))
523 char token[50], name[50], groupName[50], label[80], options[100], *line;
530 line = strtok((char*)dataBuffer, "\n");
534 sscanf(line, "%s", token);
536 if (strcmp(token, "END") == 0)
539 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
547 if (strcmp(token, widgetName[i]) == 0)
550 if (strcmp("-1", widgetName[i]) == 0)
556 widget->setProperties(name, groupName, label, options, x, y, min, max);
561 if ((line = strtok(NULL, "\n")) == NULL)
565 highlightedWidget = (Widget*)widgetList.getHead()->next;
570 Widget *Engine::getWidgetByName(const char *name)
572 Widget *widget = (Widget*)widgetList.getHead();
574 while (widget->next != NULL)
576 widget = (Widget*)widget->next;
578 if (strcmp(widget->name, name) == 0)
582 debug(("No such widget '%s'\n", name));
587 void Engine::showWidgetGroup(const char *groupName, bool show)
591 Widget *widget = (Widget*)widgetList.getHead();
593 while (widget->next != NULL)
595 widget = (Widget*)widget->next;
597 if (strcmp(widget->groupName, groupName) == 0)
599 widget->visible = show;
606 debug(("Group '%s' does not exist\n", groupName));
609 void Engine::enableWidgetGroup(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->enabled = show;
628 debug(("Group '%s' does not exist\n", groupName));
631 void Engine::showWidget(const char *name, bool show)
633 Widget *widget = getWidgetByName(name);
636 widget->visible = show;
641 void Engine::enableWidget(const char *name, bool enable)
643 Widget *widget = getWidgetByName(name);
646 widget->enabled = enable;
651 void Engine::setWidgetVariable(const char *name, int *variable)
653 Widget *widget = getWidgetByName(name);
655 widget->value = variable;
658 bool Engine::widgetChanged(const char *name)
660 Widget *widget = getWidgetByName(name);
662 return widget->changed;
667 void Engine::highlightWidget(int dir)
669 highlightedWidget->redraw();
675 if (highlightedWidget->next != NULL)
677 highlightedWidget = (Widget*)highlightedWidget->next;
681 highlightedWidget = (Widget*)widgetList.getHead()->next;
684 if (highlightedWidget->type == 4)
687 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
696 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
698 highlightedWidget = highlightedWidget->previous;
702 highlightedWidget = (Widget*)widgetList.getTail();
705 if (highlightedWidget->type == WG_LABEL)
708 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
713 highlightedWidget->redraw();
716 void Engine::highlightWidget(const char *name)
718 highlightedWidget = getWidgetByName(name);
721 int Engine::processWidgets()
725 if (keyState[SDLK_UP])
732 if (keyState[SDLK_DOWN])
739 if (keyState[SDLK_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
743 if (*highlightedWidget->value > highlightedWidget->min)
745 *highlightedWidget->value = *highlightedWidget->value - 1;
747 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
749 highlightedWidget->changed = true;
752 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
756 if (keyState[SDLK_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
760 if (*highlightedWidget->value < highlightedWidget->max)
762 *highlightedWidget->value = *highlightedWidget->value + 1;
764 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
766 highlightedWidget->changed = true;
769 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
773 if ((keyState[SDLK_RETURN]) || (keyState[SDLK_SPACE]) || (keyState[SDLK_LCTRL]))
775 if (highlightedWidget->value == NULL)
777 debug(("%s has not been implemented!\n", highlightedWidget->name));
781 if (highlightedWidget->type == WG_BUTTON)
783 *highlightedWidget->value = 1;
784 highlightedWidget->changed = true;
786 else if (highlightedWidget->type == WG_JOYPAD)
788 waitForButton = true;
792 if (*highlightedWidget->value > -1000)
794 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
797 else if (highlightedWidget->type == WG_KEYBOARD)
800 waitForButton = false;
802 *highlightedWidget->value = -*highlightedWidget->value;
817 char *strtok_r(char *s1, const char *s2, char **lasts)
826 while (*s1 && strchr(s2, *s1))
838 while(*s1 && !strchr(s2, *s1))
855 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
856 game at compile time and run time, so everything is syncronised. This technique has the advantage of
857 allowing the game's data to be human readable and easy to maintain.
859 bool Engine::loadDefines()
861 char string[2][1024];
863 if (!loadData("data/defines.h"))
866 char *token = strtok((char*)dataBuffer, "\n");
872 token = strtok(NULL, "\n");
876 if (!strstr(token, "/*"))
878 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
880 data->set(string[0], string[1], 1, 1);
881 defineList.add(data);
889 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
890 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
891 1) It makes the game data human readable and 2) It means if I change a #define in
892 the code, I don't have to change all the data entries too. You probably already
893 thought of that though... :)
895 int Engine::getValueOfDefine(const char *word)
899 Data *data = (Data*)defineList.getHead();
901 while (data->next != NULL)
903 data = (Data*)data->next;
905 if (strcmp(data->key, word) == 0)
907 rtn = atoi(data->value);
912 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
917 Does the opposite of the above(!)
919 char *Engine::getDefineOfValue(const char *prefix, int value)
923 Data *data = (Data*)defineList.getHead();
925 while (data->next != NULL)
927 data = (Data*)data->next;
929 if (strstr(data->key, prefix))
931 rtn = atoi(data->value);
940 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
945 I like this function. It receives a list of flags declared by their #define name... like
946 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
947 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
948 the values together and then returns them... phew! Makes data files human readable though :)
950 int Engine::getValueOfFlagTokens(const char *realLine)
952 if (strcmp(realLine, "0") == 0)
959 strlcpy(line, realLine, sizeof line);
963 char *word = strtok_r(line, "+", &store);
967 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
975 data = (Data*)defineList.getHead();
978 while (data->next != NULL)
980 data = (Data*)data->next;
982 if (strcmp(data->key, word) == 0)
985 sscanf(data->value, "%d", &value);
989 sscanf(data->value, "%*s %*d %*s %d", &value);
1001 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1002 #if IGNORE_FLAGTOKEN_ERRORS
1009 word = strtok_r(NULL, "+", &store);
1017 void Engine::delay(unsigned int frameLimit) {
1018 unsigned int ticks = SDL_GetTicks();
1020 if(frameLimit < ticks)
1023 if(frameLimit > ticks + 16)
1026 SDL_Delay(frameLimit - ticks);