diff --git a/client/CCursorHandler.cpp b/client/CCursorHandler.cpp index 64eccf85a..43cb24a84 100644 --- a/client/CCursorHandler.cpp +++ b/client/CCursorHandler.cpp @@ -183,6 +183,14 @@ void CCursorHandler::shiftPos( int &x, int &y ) } } +void CCursorHandler::centerCursor() +{ + SDL_Surface *cursor = this->cursors[mode]->ourImages[number].bitmap; + this->xpos = (screen->w / 2.) - (cursor->w / 2.); + this->ypos = (screen->h / 2.) - (cursor->h / 2.); + SDL_WarpMouse(this->xpos, this->ypos); +} + CCursorHandler::~CCursorHandler() { if(help) diff --git a/client/CCursorHandler.h b/client/CCursorHandler.h index a7ff66222..d136e0088 100644 --- a/client/CCursorHandler.h +++ b/client/CCursorHandler.h @@ -35,8 +35,9 @@ public: void shiftPos( int &x, int &y ); void draw2(); - void hide(){Show=0;}; - void show(){Show=1;}; + void hide() { Show=0; }; + void show() { Show=1; }; + void centerCursor(); ~CCursorHandler(); }; diff --git a/client/CMT.cpp b/client/CMT.cpp index cc9a9e16b..f3902c1a0 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -266,7 +266,7 @@ int main(int argc, char** argv) playIntro(); SDL_FillRect(screen,NULL,0); - SDL_Flip(screen); + CSDL_Ext::update(screen); loading.join(); tlog0<<"Initialization of VCMI (together): "<(false); sysOpts = GDefaultOptions; - //initializing framerate keeper - //framerate keeper initialized cingconsole = new CInGameConsole; terminate_cond.set(false); firstCall = 1; //if loading will be overwritten in serialize @@ -283,7 +281,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) initMovement(details, ho, hp); //first initializing done - SDL_framerateDelay(GH.mainFPSmng); // after first move + GH.mainFPSmng->framerateDelay(); // after first move //main moving for(int i=1; i<32; i+=2*sysOpts.heroMoveSpeed) @@ -292,7 +290,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details) adventureInt->updateScreen = true; adventureInt->show(screen); CSDL_Ext::update(screen); - SDL_framerateDelay(GH.mainFPSmng); //for animation purposes + GH.mainFPSmng->framerateDelay(); //for animation purposes } //for(int i=1; i<32; i+=4) //main moving done @@ -1330,18 +1328,11 @@ void CPlayerInterface::update() if(adventureInt && !adventureInt->selection && GH.topInt() == adventureInt) return; - GH.updateTime(); - GH.handleEvents(); - if(adventureInt && !adventureInt->isActive() && adventureInt->scrollingDir) //player forces map scrolling though interface is disabled GH.totalRedraw(); else GH.simpleRedraw(); - CCS->curh->draw1(); - CSDL_Ext::update(screen); - CCS->curh->draw2(); - pim->unlock(); } diff --git a/client/CPreGame.cpp b/client/CPreGame.cpp index b057683cc..f8f9748da 100644 --- a/client/CPreGame.cpp +++ b/client/CPreGame.cpp @@ -351,12 +351,7 @@ void CGPreGame::update() if(SEL) SEL->update(); - CCS->curh->draw1(); - SDL_Flip(screen); - CCS->curh->draw2(); GH.topInt()->show(screen); - GH.updateTime(); - GH.handleEvents(); } CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map *Names /*= NULL*/) diff --git a/client/GUIBase.cpp b/client/GUIBase.cpp index 0014e577c..a0ca7382d 100644 --- a/client/GUIBase.cpp +++ b/client/GUIBase.cpp @@ -103,6 +103,11 @@ IShowActivable * CGuiHandler::topInt() } void CGuiHandler::totalRedraw() +{ + this->invalidateTotalRedraw = true; +} + +void CGuiHandler::internalTotalRedraw() { for(int i=0;ishowAll(screen2); @@ -111,6 +116,9 @@ void CGuiHandler::totalRedraw() if(objsToBlit.size()) objsToBlit.back()->showAll(screen); + + this->invalidateTotalRedraw = false; + this->invalidateSimpleRedraw = false; } void CGuiHandler::updateTime() @@ -303,11 +311,18 @@ void CGuiHandler::handleMouseMotion(SDL_Event *sEvent) } void CGuiHandler::simpleRedraw() +{ + this->invalidateSimpleRedraw = true; +} + +void CGuiHandler::internalSimpleRedraw() { //update only top interface and draw background if(objsToBlit.size() > 1) blitAt(screen2,0,0,screen); //blit background objsToBlit.back()->show(screen); //blit active interface/window + + this->invalidateSimpleRedraw = false; } void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion ) @@ -343,14 +358,33 @@ void CGuiHandler::run() setThreadName(-1, "CGuiHandler::run"); try { - SDL_initFramerate(mainFPSmng); + CCS->curh->centerCursor(); + mainFPSmng->init(); // resets internal clock, needed for FPS manager while(!terminate) { if(curInt) - curInt->update(); + curInt->update(); // calls a update and drawing process of the loaded game interface object at the moment - SDL_framerateDelay(mainFPSmng); - //SDL_Delay(20); //give time for other apps + // Handles mouse and key input + GH.updateTime(); + GH.handleEvents(); + + // Redraws the GUI only once during rendering + if (this->invalidateTotalRedraw == true) + internalTotalRedraw(); + if (this->invalidateSimpleRedraw == true) + internalSimpleRedraw(); + + if (SHOW_FPS) + drawFPSCounter(); + + mainFPSmng->framerateDelay(); // holds a constant FPS + + // draw the mouse cursor and update the screen + // todo: bad way of updating the cursor, update screen should be the last statement of the rendering process + CCS->curh->draw1(); + CSDL_Ext::update(screen); + CCS->curh->draw2(); } } HANDLE_EXCEPTION } @@ -363,8 +397,8 @@ CGuiHandler::CGuiHandler() terminate = false; statusbar = NULL; - mainFPSmng = new FPSmanager; - SDL_setFramerate(mainFPSmng, 48); + // Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate + mainFPSmng = new FPSManager(48); } CGuiHandler::~CGuiHandler() @@ -377,6 +411,17 @@ void CGuiHandler::breakEventHandling() current = NULL; } +void CGuiHandler::drawFPSCounter() +{ + const static SDL_Color yellow = {255, 255, 0, 0}; + static SDL_Rect overlay = { 0, 0, 64, 32}; + Uint32 black = SDL_MapRGB(screen->format, 10, 10, 10); + SDL_FillRect(screen, &overlay, black); + std::string fps = toString(mainFPSmng->fps); + CSDL_Ext::printAt(fps, 10, 10, FONT_BIG, yellow, screen); +} + + void CIntObject::activateLClick() { GH.lclickable.push_front(this); diff --git a/client/GUIBase.h b/client/GUIBase.h index 0cd7ca899..8b5bb74fc 100644 --- a/client/GUIBase.h +++ b/client/GUIBase.h @@ -511,8 +511,16 @@ public: /// Handles GUI logic and drawing class CGuiHandler { +private: + bool invalidateTotalRedraw; + bool invalidateSimpleRedraw; + + void internalTotalRedraw(); + void internalSimpleRedraw(); + public: - FPSmanager * mainFPSmng; //to keep const framerate + const static bool SHOW_FPS = false; // shows a fps counter when set to true + FPSManager *mainFPSmng; //to keep const framerate timeHandler th; std::list listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on) IStatusBar * statusbar; @@ -539,14 +547,17 @@ public: CGuiHandler(); ~CGuiHandler(); - void run(); - void totalRedraw(); //forces total redraw (using showAll) - void simpleRedraw(); //update only top interface and draw background from buffer + void run(); // holds the main loop for the whole program after initialization and manages the update/rendering system + + void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering + void simpleRedraw(); //update only top interface and draw background from buffer, sets a flag, method gets called at the end of the rendering + void popInt(IShowActivable *top); //removes given interface from the top and activates next void popIntTotally(IShowActivable *top); //deactivates, deletes, removes given interface from the top and activates next void pushInt(IShowActivable *newInt); //deactivate old top interface, activates this one and pushes to the top void popInts(int howMany); //pops one or more interfaces - deactivates top, deletes and removes given number of interfaces, activates new front IShowActivable *topInt(); //returns top interface + void updateTime(); //handles timeInterested void handleEvents(); //takes events from queue and calls interested objects void handleEvent(SDL_Event *sEvent); @@ -554,6 +565,7 @@ public: void handleMoveInterested( const SDL_MouseMotionEvent & motion ); void fakeMouseMove(); void breakEventHandling(); //current event won't be propagated anymore + void CGuiHandler::drawFPSCounter(); // draws the FPS to the upper left corner of the screen ui8 defActionsDef; //default auto actions ui8 captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list std::list createdObj; //stack of objs being created diff --git a/client/SDL_framerate.cpp b/client/SDL_framerate.cpp index 5edcf45e0..d76b38776 100644 --- a/client/SDL_framerate.cpp +++ b/client/SDL_framerate.cpp @@ -1,84 +1,41 @@ -/* - - SDL_framerate: framerate manager - - LGPL (c) A. Schiffler - +/* + * SDL_framerate.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * */ + #include "../stdafx.h" #include "SDL_framerate.h" +#include -/* - Initialize the framerate manager -*/ -void SDL_initFramerate(FPSmanager * manager) +FPSManager::FPSManager(int rate) { - /* - * Store some sane values - */ - manager->framecount = 0; - manager->rate = FPS_DEFAULT; - manager->rateticks = (1000.0 / (float) FPS_DEFAULT); - manager->lastticks = SDL_GetTicks(); + this->rate = rate; + this->rateticks = (1000.0 / (double) rate); + this->fps = 0; } -/* - Set the framerate in Hz -*/ - -int SDL_setFramerate(FPSmanager * manager, int rate) +void FPSManager::init() { - if ((rate >= FPS_LOWER_LIMIT) && (rate <= FPS_UPPER_LIMIT)) { - manager->framecount = 0; - manager->rate = rate; - manager->rateticks = (1000.0 / (float) rate); - return (0); - } else { - return (-1); - } + this->lastticks = SDL_GetTicks(); } -/* - Return the current target framerate in Hz -*/ - -int SDL_getFramerate(FPSmanager * manager) +void FPSManager::framerateDelay() { - if (manager == NULL) { - return (-1); - } else { - return (manager->rate); - } -} - -/* - Delay execution to maintain a constant framerate. Calculate fps. -*/ - -void SDL_framerateDelay(FPSmanager * manager) -{ - Uint32 current_ticks; - Uint32 target_ticks; - Uint32 the_delay; - - /* - * Next frame - */ - manager->framecount++; - - /* - * Get/calc ticks - */ - current_ticks = SDL_GetTicks(); - target_ticks = manager->lastticks + (Uint32) ((float) manager->framecount * manager->rateticks); - - if (current_ticks <= target_ticks) { - the_delay = target_ticks - current_ticks; - SDL_Delay(the_delay); - } else { - manager->framecount = 0; - manager->lastticks = SDL_GetTicks(); - } + Uint32 currentTicks = SDL_GetTicks(); + double diff = currentTicks - this->lastticks; + + if (diff < this->rateticks) // FPS is higher than it should be, then wait some time + { + SDL_Delay(ceil(this->rateticks) - diff); + } + + this->fps = ceil(1000. / (SDL_GetTicks() - this->lastticks)); + this->lastticks = SDL_GetTicks(); } diff --git a/client/SDL_framerate.h b/client/SDL_framerate.h index 3de7ac5c4..daa88ac83 100644 --- a/client/SDL_framerate.h +++ b/client/SDL_framerate.h @@ -1,65 +1,33 @@ -/* - - SDL_framerate: framerate manager - - LGPL (c) A. Schiffler - +/* + * timeHandler.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * */ #ifndef _SDL_framerate_h #define _SDL_framerate_h -/* Set up for C function definitions, even when using C++ */ -#ifdef __cplusplus -extern "C" { + +/// A fps manager which holds game updates at a constant rate +class FPSManager +{ +private: + double rateticks; + unsigned int lastticks; + int rate; + +public: + int fps; // the actual fps value + + FPSManager(int rate); // initializes the manager with a given fps rate + void FPSManager::init(); // needs to be called directly before the main game loop to reset the internal timer + void framerateDelay(); // needs to be called every game update cycle +}; + + #endif - -/* --- */ - -#include "SDL.h" - -/* --------- Definitions */ - -/* Some rates in Hz */ - -#define FPS_UPPER_LIMIT 200 -#define FPS_LOWER_LIMIT 1 -#define FPS_DEFAULT 30 - -/* --------- Structure variables */ - - typedef struct { - Uint32 framecount; - float rateticks; - Uint32 lastticks; - Uint32 rate; - } FPSmanager; - -/* --------- Function prototypes */ - -#ifdef WIN32 -#ifdef BUILD_DLL -#define DLLINTERFACE __declspec(dllexport) -#else -#define DLLINTERFACE __declspec(dllimport) -#endif -#else -#define DLLINTERFACE -#endif - -/* Functions return 0 or value for sucess and -1 for error */ - - void SDL_initFramerate(FPSmanager * manager); - int SDL_setFramerate(FPSmanager * manager, int rate); - int SDL_getFramerate(FPSmanager * manager); - void SDL_framerateDelay(FPSmanager * manager); - -/* --- */ - -/* Ends C function definitions when using C++ */ -#ifdef __cplusplus -} -#endif - -#endif /* _SDL_framerate_h */ diff --git a/global.h b/global.h index ccf66e97b..96da25147 100644 --- a/global.h +++ b/global.h @@ -135,7 +135,6 @@ const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; const int SPELLBOOK_GOLD_COST = 500; - //for battle stacks' positions struct THex { @@ -348,6 +347,17 @@ enum EAlignment { GOOD, EVIL, NEUTRAL }; + +// Converts an int/double or any data type you wish to a string +template +std::string toString(const T& value) +{ + std::ostringstream oss; + oss << value; + return oss.str(); +} + + //uncomment to make it work //#define MARK_BLOCKED_POSITIONS //#define MARK_VISITABLE_POSITIONS