/// main loop and framerate control /* mass - a game without bitmaps Copyright (C) 2001-2003 Martin Renold This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "main.h" #include "tests.h" #include "video2d.h" #include "video3d.h" #include "debug.h" #include "network.h" #include "input.h" #include "random.h" #include "config.h" #include "stringio.h" #include "engine.h" #include "level.h" // for the timer #include "SDL.h" #include #include #include #include // globals int debugMode = 0; int paused = 0; // tries to start newFrontend, and returns the one actually started int StartFrontend(Frontend oldFrontend, Frontend newFrontend) { int i; if (newFrontend == F_WRAP) newFrontend = 1; if (oldFrontend == newFrontend) return oldFrontend; if (oldFrontend == F_2D) { DoneVideo2d(); FreeFrontendMemory(); } if (oldFrontend == F_3D) { DoneVideo3d(); FreeFrontendMemory(); } if (newFrontend == F_NONE) return F_NONE; for (i=0; itoggled || (!config.fullscreen)); return newFrontend; } if (newFrontend == F_3D && !InitVideo3d()) { SDL_ShowCursor(key.debugMode->toggled || (!config.fullscreen)); return newFrontend; } newFrontend++; if (newFrontend == F_WRAP) newFrontend = 1; } Die("Did not find any way to show the game, sorry :-(\n"); return 0; // keep the compiler happy } int main(int argc, char **argv) { int frameno = 0; #define TIME_PRECISION 16 // beware the overflow int framesDropped = 0; Frontend frontend; Uint32 minFrameDuration = 0; Uint32 lastFrameTime; // in miliseconds*TIME_PRECISION since SDL_Init printf("mass version %s\n", VERSIONNAME); Assertions(); if (argc == 2 && streq(argv[1], "--tests")) return tests(); #ifdef DEBUG SDL_Init(SDL_INIT_NOPARACHUTE); #else SDL_Init(0); #endif // SDL needs video initialized before the keymap to read the // input config file ist available if (SDL_InitSubSystem(SDL_INIT_VIDEO)) Die("Could not initialize the SDL video subsystem: %s\n", SDL_GetError()); InitInput(); InitConfig(argc, argv); SDL_QuitSubSystem(SDL_INIT_VIDEO); srand(time(NULL)); synced_srandom(time(NULL)); if (config.exitFrame) { srand(0); synced_srandom(0); } #ifndef HEAVY_DEBUG if (config.heavyDebug != -1) Die("Compiled without HEAVY_DEBUG!\n"); #endif debugMode = 0; frontend = config.display; if (frontend == F_NONE) printf("No video requested!\n"); if (config.maxCPS <= 0) { minFrameDuration = 1; DebugLevel(1, "No maximum CPS, always running at full speed.\n"); } else { minFrameDuration = (TIME_PRECISION*1000) / config.maxCPS; DebugLevel(1, "Max CPS set to %d (which means minimum cycle duration %d/%dms)\n", config.maxCPS, minFrameDuration,TIME_PRECISION); } InitEngine(); InitLevel(); InitNetwork(); /* the engine will be initialized in network.ch with the first UpdatePlayerInput() */ /* tick, tack. */ lastFrameTime = /* lastShownFrameTime = */ TIME_PRECISION * SDL_GetTicks(); /* pick and start a frontend to display the game */ frontend = StartFrontend(F_NONE, frontend); while (1) { int framedrop = 0; if (!paused || key.nextFrame->hit) { UpdatePlayerInput(frameno++); if (frameno == config.heavyDebug) config.heavyDebug = 0; if (key.nextFrame->hit || key.nextFrame->pressed || HEAVY_DEBUG_ENABLED) DebugLevel(2, "Engine frame %d starts.\n", frameno); /* delay the completion, or set framedrop */ if (minFrameDuration) { Uint32 time; time = TIME_PRECISION * SDL_GetTicks(); /* sleep if it is running too fast */ while (minFrameDuration > time - lastFrameTime) { // FIXME: test whether removing this makes a difference on fast/normal/slow systems SDL_Delay((minFrameDuration - (time - lastFrameTime))/TIME_PRECISION); //printf("SDL_Delay(%d)\n", (minFrameDuration - (time - lastFrameTime))/TIME_PRECISION); time = TIME_PRECISION * SDL_GetTicks(); } assert(time >= lastFrameTime + minFrameDuration); lastFrameTime += minFrameDuration; /* if the engine lags behind > 100ms, start dropping frames */ if (time - lastFrameTime > TIME_PRECISION*100) framedrop = 1; if (paused) framedrop = 0; if (framedrop && framesDropped > config.maxSkip) { static int maxskip_show = 0; //printf("Drawing enforced with --maxskip:\n"); if (frameno > 500) { maxskip_show++; } framedrop = 0; /* the --maxcps value cannot be hold, reset the timer */ lastFrameTime = time - TIME_PRECISION*100; if (maxskip_show == 30 && config.maxCPS) { /* mplayer-style complaint :-) */ printf("\n"); printf(" ************************************************\n"); printf(" **** Your system is too SLOW to play this! ****\n"); printf(" ************************************************\n"); printf("!!! Possible reasons, problems, workarounds: \n"); printf("- The game has currently to calculate more masses than usual. You\n"); printf(" could simply wait until some of them disappear, or move away.\n"); printf("- Give a smaller, more realistic value to --maxcps (see --help)\n"); printf("- If it keeps really unplayable, decrease the --maxskip parameter.\n"); printf("- If you are playing a network game, this could also be because an other\n"); printf(" player runs slower (different --maxcps parameter (FIXME: synchronize them\n"); printf(" automatically) or too slow machine), and you are waiting for the other\n"); printf(" player's packages (FIXME: use this time to show more frames instead)\n"); printf("- If you use OpenGL (3D), read in the README how to decrease the detaillevel.\n"); printf("- Try the compile-time optimizations mentioned in the README.\n"); printf("- Optimize the code.\n"); printf("- Get a faster computer.\n"); } } if (framedrop) { framesDropped++; } else { framesDropped = 0; //lastShownFrameTime = lastFrameTime; } } if (config.exitFrame && frameno == config.exitFrame) { printf("Frame %d (requested exitFrame), game checksum is %d.\n", (int)frameno, FullChecksum()); break; } /* let the engine work */ NextGameFrame(); } if (!framedrop) { //printf("Drawing frame %d\n", frameno); UpdateDisplayCenter(); if (frontend == F_2D) DrawFrame2d(); if (frontend == F_3D) DrawFrame3d(); /* get new keyboard and mouse state */ /* only once each shown frame */ if (paused) SDL_WaitEvent(NULL); if (frontend != F_NONE) UpdateInput(); /* keyboard handling goes here */ if (key.pause->hit) paused = !paused; if (key.switchInterface->hit) frontend = StartFrontend(frontend, frontend+1); if (key.quit->hit) break; if (key.fullscreen->hit) { config.fullscreen = !config.fullscreen; StartFrontend(frontend, F_NONE); StartFrontend(F_NONE, frontend); } if (key.debugMode->hit) { debugMode = !debugMode; if (debugMode) { SDL_ShowCursor(SDL_ENABLE); } else { SDL_ShowCursor(config.fullscreen ? SDL_DISABLE : SDL_ENABLE); } // needed because some keys might have another function in debug mode ResetKeys(); } if (key.saveGame->hit) SaveGame("save"); if (key.debugStatistics->hit) PrintFieldStatistics(); } } frontend = StartFrontend(frontend, F_NONE); FreeNetwork(); FreeLevel(); FreeEngine(); FreeConfig(); DoneInput(); SDL_Quit(); exit(0); }