1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-17 13:41:07 +02:00

Merge pull request #2153 from IvanSavenko/better_time_management

Separate updates from rendering actions
This commit is contained in:
Ivan Savenko 2023-05-16 19:05:40 +03:00 committed by GitHub
commit ddf22a757d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 285 additions and 190 deletions

View File

@ -599,8 +599,6 @@ static void mainLoop()
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); }); fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
inGuiThread.reset(new bool(true)); inGuiThread.reset(new bool(true));
assert(GH.mainFPSmng);
GH.mainFPSmng->init(settings["video"]["targetfps"].Integer());
while(1) //main SDL events loop while(1) //main SDL events loop
{ {

View File

@ -31,6 +31,7 @@ set(client_SRCS
gui/CIntObject.cpp gui/CIntObject.cpp
gui/CursorHandler.cpp gui/CursorHandler.cpp
gui/InterfaceObjectConfigurable.cpp gui/InterfaceObjectConfigurable.cpp
gui/FramerateManager.cpp
gui/NotificationHandler.cpp gui/NotificationHandler.cpp
gui/ShortcutHandler.cpp gui/ShortcutHandler.cpp
@ -162,6 +163,7 @@ set(client_HEADERS
gui/CIntObject.h gui/CIntObject.h
gui/CursorHandler.h gui/CursorHandler.h
gui/InterfaceObjectConfigurable.h gui/InterfaceObjectConfigurable.h
gui/FramerateManager.h
gui/MouseButton.h gui/MouseButton.h
gui/NotificationHandler.h gui/NotificationHandler.h
gui/Shortcut.h gui/Shortcut.h

View File

@ -12,6 +12,7 @@
#include "CMT.h" #include "CMT.h"
#include "gui/CGuiHandler.h" #include "gui/CGuiHandler.h"
#include "gui/FramerateManager.h"
#include "renderSDL/SDL_Extensions.h" #include "renderSDL/SDL_Extensions.h"
#include "CPlayerInterface.h" #include "CPlayerInterface.h"
#include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/Filesystem.h"
@ -370,7 +371,7 @@ void CVideoPlayer::update( int x, int y, SDL_Surface *dst, bool forceRedraw, boo
auto packet_duration = frame->duration; auto packet_duration = frame->duration;
#endif #endif
double frameEndTime = (frame->pts + packet_duration) * av_q2d(format->streams[stream]->time_base); double frameEndTime = (frame->pts + packet_duration) * av_q2d(format->streams[stream]->time_base);
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.0; frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.0;
if (frameTime >= frameEndTime ) if (frameTime >= frameEndTime )
{ {
@ -450,6 +451,7 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
pos.x = x; pos.x = x;
pos.y = y; pos.y = y;
frameTime = 0.0;
while(nextFrame()) while(nextFrame())
{ {
@ -461,10 +463,15 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
SDL_RenderCopy(mainRenderer, texture, nullptr, &rect); SDL_RenderCopy(mainRenderer, texture, nullptr, &rect);
SDL_RenderPresent(mainRenderer); SDL_RenderPresent(mainRenderer);
// Wait 3 frames #if (LIBAVUTIL_VERSION_MAJOR < 58)
GH.mainFPSmng->framerateDelay(); auto packet_duration = frame->pkt_duration;
GH.mainFPSmng->framerateDelay(); #else
GH.mainFPSmng->framerateDelay(); auto packet_duration = frame->duration;
#endif
double frameDurationSec = packet_duration * av_q2d(format->streams[stream]->time_base);
uint32_t timeToSleepMillisec = 1000 * (frameDurationSec);
boost::this_thread::sleep(boost::posix_time::millisec(timeToSleepMillisec));
} }
return true; return true;

View File

@ -59,7 +59,7 @@ AdventureMapInterface::AdventureMapInterface():
shortcuts->setState(EAdventureState::MAKING_TURN); shortcuts->setState(EAdventureState::MAKING_TURN);
widget->getMapView()->onViewMapActivated(); widget->getMapView()->onViewMapActivated();
addUsedEvents(KEYBOARD); addUsedEvents(KEYBOARD | TIME);
} }
void AdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel) void AdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel)
@ -139,18 +139,20 @@ void AdventureMapInterface::showAll(SDL_Surface * to)
void AdventureMapInterface::show(SDL_Surface * to) void AdventureMapInterface::show(SDL_Surface * to)
{ {
handleMapScrollingUpdate();
CIntObject::show(to); CIntObject::show(to);
LOCPLINT->cingconsole->show(to); LOCPLINT->cingconsole->show(to);
} }
void AdventureMapInterface::handleMapScrollingUpdate() void AdventureMapInterface::tick(uint32_t msPassed)
{
handleMapScrollingUpdate(msPassed);
}
void AdventureMapInterface::handleMapScrollingUpdate(uint32_t timePassed)
{ {
/// Width of window border, in pixels, that triggers map scrolling /// Width of window border, in pixels, that triggers map scrolling
static constexpr uint32_t borderScrollWidth = 15; static constexpr uint32_t borderScrollWidth = 15;
uint32_t timePassed = GH.mainFPSmng->getElapsedMilliseconds();
uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float(); uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float();
uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000; uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000;

View File

@ -75,7 +75,7 @@ private:
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
/// check and if necessary reacts on scrolling by moving cursor to screen edge /// check and if necessary reacts on scrolling by moving cursor to screen edge
void handleMapScrollingUpdate(); void handleMapScrollingUpdate(uint32_t msPassed);
void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode); void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
@ -93,6 +93,7 @@ protected:
void activate() override; void activate() override;
void deactivate() override; void deactivate() override;
void tick(uint32_t msPassed) override;
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;

View File

@ -222,7 +222,7 @@ bool DummyAnimation::init()
return true; return true;
} }
void DummyAnimation::nextFrame() void DummyAnimation::tick(uint32_t msPassed)
{ {
counter++; counter++;
if(counter > howMany) if(counter > howMany)
@ -300,7 +300,7 @@ ECreatureAnimType MeleeAttackAnimation::selectGroup(bool multiAttack)
return mutPosToGroup[mutPos]; return mutPosToGroup[mutPos];
} }
void MeleeAttackAnimation::nextFrame() void MeleeAttackAnimation::tick(uint32_t msPassed)
{ {
size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame(); size_t currentFrame = stackAnimation(attackingStack)->getCurrentFrame();
size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(getGroup()); size_t totalFrames = stackAnimation(attackingStack)->framesInGroup(getGroup());
@ -308,7 +308,7 @@ void MeleeAttackAnimation::nextFrame()
if ( currentFrame * 2 >= totalFrames ) if ( currentFrame * 2 >= totalFrames )
owner.executeAnimationStage(EAnimationEvents::HIT); owner.executeAnimationStage(EAnimationEvents::HIT);
AttackAnimation::nextFrame(); AttackAnimation::tick(msPassed);
} }
MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack) MeleeAttackAnimation::MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack)
@ -379,15 +379,15 @@ bool MovementAnimation::init()
return true; return true;
} }
void MovementAnimation::nextFrame() void MovementAnimation::tick(uint32_t msPassed)
{ {
progress += float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000 * progressPerSecond; progress += float(msPassed) / 1000 * progressPerSecond;
//moving instructions //moving instructions
myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress ); myAnim->pos.x = static_cast<Sint16>(begX + distanceX * progress );
myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress ); myAnim->pos.y = static_cast<Sint16>(begY + distanceY * progress );
BattleAnimation::nextFrame(); BattleAnimation::tick(msPassed);
if(progress >= 1.0) if(progress >= 1.0)
{ {
@ -577,9 +577,9 @@ bool ColorTransformAnimation::init()
return true; return true;
} }
void ColorTransformAnimation::nextFrame() void ColorTransformAnimation::tick(uint32_t msPassed)
{ {
float elapsed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; float elapsed = msPassed / 1000.f;
float fullTime = AnimationControls::getFadeInDuration(); float fullTime = AnimationControls::getFadeInDuration();
float delta = elapsed / fullTime; float delta = elapsed / fullTime;
totalProgress += delta; totalProgress += delta;
@ -699,7 +699,7 @@ void RangedAttackAnimation::emitProjectile()
projectileEmitted = true; projectileEmitted = true;
} }
void RangedAttackAnimation::nextFrame() void RangedAttackAnimation::tick(uint32_t msPassed)
{ {
// animation should be paused if there is an active projectile // animation should be paused if there is an active projectile
if (projectileEmitted) if (projectileEmitted)
@ -716,7 +716,7 @@ void RangedAttackAnimation::nextFrame()
else else
stackAnimation(attackingStack)->playUntil(static_cast<size_t>(-1)); stackAnimation(attackingStack)->playUntil(static_cast<size_t>(-1));
AttackAnimation::nextFrame(); AttackAnimation::tick(msPassed);
if (!projectileEmitted) if (!projectileEmitted)
{ {
@ -790,9 +790,9 @@ CatapultAnimation::CatapultAnimation(BattleInterface & owner, const CStack * att
logAnim->debug("Created shooting anim for %s", stack->getName()); logAnim->debug("Created shooting anim for %s", stack->getName());
} }
void CatapultAnimation::nextFrame() void CatapultAnimation::tick(uint32_t msPassed)
{ {
ShootingAnimation::nextFrame(); ShootingAnimation::tick(msPassed);
if ( explosionEmitted) if ( explosionEmitted)
return; return;
@ -988,9 +988,9 @@ bool EffectAnimation::init()
return true; return true;
} }
void EffectAnimation::nextFrame() void EffectAnimation::tick(uint32_t msPassed)
{ {
playEffect(); playEffect(msPassed);
if (effectFinished) if (effectFinished)
{ {
@ -1020,7 +1020,7 @@ void EffectAnimation::onEffectFinished()
effectFinished = true; effectFinished = true;
} }
void EffectAnimation::playEffect() void EffectAnimation::playEffect(uint32_t msPassed)
{ {
if ( effectFinished ) if ( effectFinished )
return; return;
@ -1029,7 +1029,7 @@ void EffectAnimation::playEffect()
{ {
if(elem.effectID == ID) if(elem.effectID == ID)
{ {
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000; elem.currentFrame += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
if(elem.currentFrame >= elem.animation->size()) if(elem.currentFrame >= elem.animation->size())
{ {
@ -1113,7 +1113,7 @@ void HeroCastAnimation::emitAnimationEvent()
owner.executeAnimationStage(EAnimationEvents::HIT); owner.executeAnimationStage(EAnimationEvents::HIT);
} }
void HeroCastAnimation::nextFrame() void HeroCastAnimation::tick(uint32_t msPassed)
{ {
float frame = hero->getFrame(); float frame = hero->getFrame();

View File

@ -48,7 +48,7 @@ public:
bool isInitialized(); bool isInitialized();
bool tryInitialize(); bool tryInitialize();
virtual void nextFrame() {} //call every new frame virtual void tick(uint32_t msPassed) {} //call every new frame
virtual ~BattleAnimation(); virtual ~BattleAnimation();
BattleAnimation(BattleInterface & owner); BattleAnimation(BattleInterface & owner);
@ -120,7 +120,7 @@ class ColorTransformAnimation : public BattleStackAnimation
float totalProgress; float totalProgress;
bool init() override; bool init() override;
void nextFrame() override; void tick(uint32_t msPassed) override;
public: public:
ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell); ColorTransformAnimation(BattleInterface & owner, const CStack * _stack, const std::string & colorFilterName, const CSpell * spell);
@ -157,7 +157,7 @@ private:
public: public:
bool init() override; bool init() override;
void nextFrame() override; void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance); MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
~MovementAnimation(); ~MovementAnimation();
@ -220,7 +220,7 @@ class MeleeAttackAnimation : public AttackAnimation
public: public:
MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack); MeleeAttackAnimation(BattleInterface & owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool multiAttack);
void nextFrame() override; void tick(uint32_t msPassed) override;
}; };
@ -246,7 +246,7 @@ public:
~RangedAttackAnimation(); ~RangedAttackAnimation();
bool init() override; bool init() override;
void nextFrame() override; void tick(uint32_t msPassed) override;
}; };
/// Shooting attack /// Shooting attack
@ -275,7 +275,7 @@ public:
CatapultAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender, int _catapultDmg = 0); CatapultAnimation(BattleInterface & owner, const CStack * attacker, BattleHex dest, const CStack * defender, int _catapultDmg = 0);
void createProjectile(const Point & from, const Point & dest) const override; void createProjectile(const Point & from, const Point & dest) const override;
void nextFrame() override; void tick(uint32_t msPassed) override;
}; };
class CastAnimation : public RangedAttackAnimation class CastAnimation : public RangedAttackAnimation
@ -300,7 +300,7 @@ private:
int howMany; int howMany;
public: public:
bool init() override; bool init() override;
void nextFrame() override; void tick(uint32_t msPassed) override;
DummyAnimation(BattleInterface & owner, int howManyFrames); DummyAnimation(BattleInterface & owner, int howManyFrames);
}; };
@ -324,7 +324,7 @@ class EffectAnimation : public BattleAnimation
void onEffectFinished(); void onEffectFinished();
void clearEffect(); void clearEffect();
void playEffect(); void playEffect(uint32_t msPassed);
public: public:
enum EEffectFlags enum EEffectFlags
@ -349,7 +349,7 @@ public:
~EffectAnimation(); ~EffectAnimation();
bool init() override; bool init() override;
void nextFrame() override; void tick(uint32_t msPassed) override;
}; };
class HeroCastAnimation : public BattleAnimation class HeroCastAnimation : public BattleAnimation
@ -367,6 +367,6 @@ class HeroCastAnimation : public BattleAnimation
public: public:
HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell); HeroCastAnimation(BattleInterface & owner, std::shared_ptr<BattleHero> hero, BattleHex dest, const CStack * defender, const CSpell * spell);
void nextFrame() override; void tick(uint32_t msPassed) override;
bool init() override; bool init() override;
}; };

View File

@ -68,7 +68,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height())); backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height()));
updateAccessibleHexes(); updateAccessibleHexes();
addUsedEvents(LCLICK | RCLICK | MOVE); addUsedEvents(LCLICK | RCLICK | MOVE | TIME);
} }
void BattleFieldController::activate() void BattleFieldController::activate()
@ -134,7 +134,7 @@ void BattleFieldController::renderBattlefield(Canvas & canvas)
renderer.execute(clippedCanvas); renderer.execute(clippedCanvas);
owner.projectilesController->showProjectiles(clippedCanvas); owner.projectilesController->render(clippedCanvas);
} }
void BattleFieldController::showBackground(Canvas & canvas) void BattleFieldController::showBackground(Canvas & canvas)
@ -611,12 +611,16 @@ void BattleFieldController::showAll(SDL_Surface * to)
show(to); show(to);
} }
void BattleFieldController::show(SDL_Surface * to) void BattleFieldController::tick(uint32_t msPassed)
{ {
updateAccessibleHexes(); updateAccessibleHexes();
owner.stacksController->update(); owner.stacksController->tick(msPassed);
owner.obstacleController->update(); owner.obstacleController->tick(msPassed);
owner.projectilesController->tick(msPassed);
}
void BattleFieldController::show(SDL_Surface * to)
{
Canvas canvas(to); Canvas canvas(to);
CSDL_Ext::CClipRectGuard guard(to, pos); CSDL_Ext::CClipRectGuard guard(to, pos);

View File

@ -70,6 +70,7 @@ class BattleFieldController : public CIntObject
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void tick(uint32_t msPassed) override;
public: public:
BattleFieldController(BattleInterface & owner); BattleFieldController(BattleInterface & owner);

View File

@ -202,6 +202,25 @@ const CGHeroInstance * BattleHero::instance()
return hero; return hero;
} }
void BattleHero::tick(uint32_t msPassed)
{
size_t groupIndex = static_cast<size_t>(phase);
float timePassed = msPassed / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
if(currentFrame >= animation->size(groupIndex))
{
currentFrame -= animation->size(groupIndex);
switchToNextPhase();
}
}
void BattleHero::render(Canvas & canvas) void BattleHero::render(Canvas & canvas)
{ {
size_t groupIndex = static_cast<size_t>(phase); size_t groupIndex = static_cast<size_t>(phase);
@ -219,20 +238,6 @@ void BattleHero::render(Canvas & canvas)
canvas.draw(flagFrame, flagPosition); canvas.draw(flagFrame, flagPosition);
canvas.draw(heroFrame, heroPosition); canvas.draw(heroFrame, heroPosition);
float timePassed = float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000.f;
flagCurrentFrame += currentSpeed * timePassed;
currentFrame += currentSpeed * timePassed;
if(flagCurrentFrame >= flagAnimation->size(0))
flagCurrentFrame -= flagAnimation->size(0);
if(currentFrame >= animation->size(groupIndex))
{
currentFrame -= animation->size(groupIndex);
switchToNextPhase();
}
} }
void BattleHero::pause() void BattleHero::pause()
@ -354,6 +359,8 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
switchToNextPhase(); switchToNextPhase();
play(); play();
addUsedEvents(TIME);
} }
HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position) HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position)

View File

@ -114,6 +114,7 @@ public:
void setPhase(EHeroAnimType newPhase); //sets phase of hero animation void setPhase(EHeroAnimType newPhase); //sets phase of hero animation
void collectRenderableObjects(BattleRenderer & renderer); void collectRenderableObjects(BattleRenderer & renderer);
void tick(uint32_t msPassed) override;
float getFrame() const; float getFrame() const;
void onPhaseFinished(const std::function<void()> &); void onPhaseFinished(const std::function<void()> &);

View File

@ -159,9 +159,9 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere
} }
} }
void BattleObstacleController::update() void BattleObstacleController::tick(uint32_t msPassed)
{ {
timePassed += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; timePassed += msPassed / 1000.f;
} }
std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi) std::shared_ptr<IImage> BattleObstacleController::getObstacleImage(const CObstacleInstance & oi)

View File

@ -50,7 +50,7 @@ public:
BattleObstacleController(BattleInterface & owner); BattleObstacleController(BattleInterface & owner);
/// called every frame /// called every frame
void update(); void tick(uint32_t msPassed);
/// call-in from network pack, add newly placed obstacles with any required animations /// call-in from network pack, add newly placed obstacles with any required animations
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & oi); void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & oi);

View File

@ -58,15 +58,18 @@ void ProjectileMissile::show(Canvas & canvas)
canvas.draw(image, pos); canvas.draw(image, pos);
} }
}
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; void ProjectileMissile::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed; progress += timePassed * speed;
} }
void ProjectileAnimatedMissile::show(Canvas & canvas) void ProjectileAnimatedMissile::tick(uint32_t msPassed)
{ {
ProjectileMissile::show(canvas); ProjectileMissile::tick(msPassed);
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000; frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
size_t animationSize = animation->size(reverse ? 1 : 0); size_t animationSize = animation->size(reverse ? 1 : 0);
while (frameProgress > animationSize) while (frameProgress > animationSize)
frameProgress -= animationSize; frameProgress -= animationSize;
@ -74,9 +77,15 @@ void ProjectileAnimatedMissile::show(Canvas & canvas)
frameNum = std::floor(frameProgress); frameNum = std::floor(frameProgress);
} }
void ProjectileCatapult::tick(uint32_t msPassed)
{
frameProgress += AnimationControls::getSpellEffectSpeed() * msPassed / 1000;
float timePassed = msPassed / 1000.f;
progress += timePassed * speed;
}
void ProjectileCatapult::show(Canvas & canvas) void ProjectileCatapult::show(Canvas & canvas)
{ {
frameProgress += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
int frameCounter = std::floor(frameProgress); int frameCounter = std::floor(frameProgress);
int frameIndex = (frameCounter + 1) % animation->size(0); int frameIndex = (frameCounter + 1) % animation->size(0);
@ -90,9 +99,6 @@ void ProjectileCatapult::show(Canvas & canvas)
canvas.draw(image, pos); canvas.draw(image, pos);
} }
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
progress += timePassed * speed;
} }
void ProjectileRay::show(Canvas & canvas) void ProjectileRay::show(Canvas & canvas)
@ -135,8 +141,11 @@ void ProjectileRay::show(Canvas & canvas)
canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end); canvas.drawLine(Point(x1 + i, y1), Point(x2 + i, y2), ray.start, ray.end);
} }
} }
}
float timePassed = GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; void ProjectileRay::tick(uint32_t msPassed)
{
float timePassed = msPassed / 1000.f;
progress += timePassed * speed; progress += timePassed * speed;
} }
@ -217,13 +226,22 @@ void BattleProjectileController::emitStackProjectile(const CStack * stack)
} }
} }
void BattleProjectileController::showProjectiles(Canvas & canvas) void BattleProjectileController::render(Canvas & canvas)
{ {
for ( auto projectile: projectiles) for ( auto projectile: projectiles)
{ {
if ( projectile->playing ) if ( projectile->playing )
projectile->show(canvas); projectile->show(canvas);
} }
}
void BattleProjectileController::tick(uint32_t msPassed)
{
for ( auto projectile: projectiles)
{
if ( projectile->playing )
projectile->tick(msPassed);
}
vstd::erase_if(projectiles, [&](const std::shared_ptr<ProjectileBase> & projectile){ vstd::erase_if(projectiles, [&](const std::shared_ptr<ProjectileBase> & projectile){
return projectile->progress > 1.0f; return projectile->progress > 1.0f;

View File

@ -28,6 +28,7 @@ struct ProjectileBase
{ {
virtual ~ProjectileBase() = default; virtual ~ProjectileBase() = default;
virtual void show(Canvas & canvas) = 0; virtual void show(Canvas & canvas) = 0;
virtual void tick(uint32_t msPassed) = 0;
Point from; // initial position on the screen Point from; // initial position on the screen
Point dest; // target position on the screen Point dest; // target position on the screen
@ -42,6 +43,7 @@ struct ProjectileBase
struct ProjectileMissile : ProjectileBase struct ProjectileMissile : ProjectileBase
{ {
void show(Canvas & canvas) override; void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation; std::shared_ptr<CAnimation> animation;
int frameNum; // frame to display from projectile animation int frameNum; // frame to display from projectile animation
@ -51,7 +53,7 @@ struct ProjectileMissile : ProjectileBase
/// Projectile for spell - render animation moving in straight line from origin to destination /// Projectile for spell - render animation moving in straight line from origin to destination
struct ProjectileAnimatedMissile : ProjectileMissile struct ProjectileAnimatedMissile : ProjectileMissile
{ {
void show(Canvas & canvas) override; void tick(uint32_t msPassed) override;
float frameProgress; float frameProgress;
}; };
@ -59,6 +61,7 @@ struct ProjectileAnimatedMissile : ProjectileMissile
struct ProjectileCatapult : ProjectileBase struct ProjectileCatapult : ProjectileBase
{ {
void show(Canvas & canvas) override; void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::shared_ptr<CAnimation> animation; std::shared_ptr<CAnimation> animation;
float frameProgress; float frameProgress;
@ -68,6 +71,7 @@ struct ProjectileCatapult : ProjectileBase
struct ProjectileRay : ProjectileBase struct ProjectileRay : ProjectileBase
{ {
void show(Canvas & canvas) override; void show(Canvas & canvas) override;
void tick(uint32_t msPassed) override;
std::vector<CCreature::CreatureAnimation::RayColor> rayConfig; std::vector<CCreature::CreatureAnimation::RayColor> rayConfig;
}; };
@ -102,7 +106,10 @@ public:
BattleProjectileController(BattleInterface & owner); BattleProjectileController(BattleInterface & owner);
/// renders all currently active projectiles /// renders all currently active projectiles
void showProjectiles(Canvas & canvas); void render(Canvas & canvas);
/// updates positioning / animations of all projectiles
void tick(uint32_t msPassed);
/// returns true if stack has projectile that is yet to hit target /// returns true if stack has projectile that is yet to hit target
bool hasActiveProjectile(const CStack * stack, bool emittedOnly) const; bool hasActiveProjectile(const CStack * stack, bool emittedOnly) const;

View File

@ -335,13 +335,12 @@ void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
} }
stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit stackAnimation[stack->unitId()]->nextFrame(canvas, fullFilter, facingRight(stack)); // do actual blit
stackAnimation[stack->unitId()]->incrementFrame(float(GH.mainFPSmng->getElapsedMilliseconds()) / 1000);
} }
void BattleStacksController::update() void BattleStacksController::tick(uint32_t msPassed)
{ {
updateHoveredStacks(); updateHoveredStacks();
updateBattleAnimations(); updateBattleAnimations(msPassed);
} }
void BattleStacksController::initializeBattleAnimations() void BattleStacksController::initializeBattleAnimations()
@ -352,21 +351,30 @@ void BattleStacksController::initializeBattleAnimations()
elem->tryInitialize(); elem->tryInitialize();
} }
void BattleStacksController::stepFrameBattleAnimations() void BattleStacksController::tickFrameBattleAnimations(uint32_t msPassed)
{ {
for (auto stack : owner.curInt->cb->battleGetAllStacks(false))
{
if (stackAnimation.find(stack->unitId()) == stackAnimation.end()) //e.g. for summoned but not yet handled stacks
continue;
stackAnimation[stack->unitId()]->incrementFrame(msPassed / 1000.f);
}
// operate on copy - to prevent potential iterator invalidation due to push_back's // operate on copy - to prevent potential iterator invalidation due to push_back's
// FIXME? : remove remaining calls to addNewAnim from BattleAnimation::nextFrame (only Catapult explosion at the time of writing) // FIXME? : remove remaining calls to addNewAnim from BattleAnimation::nextFrame (only Catapult explosion at the time of writing)
auto copiedVector = currentAnimations; auto copiedVector = currentAnimations;
for (auto & elem : copiedVector) for (auto & elem : copiedVector)
if (elem && elem->isInitialized()) if (elem && elem->isInitialized())
elem->nextFrame(); elem->tick(msPassed);
} }
void BattleStacksController::updateBattleAnimations() void BattleStacksController::updateBattleAnimations(uint32_t msPassed)
{ {
bool hadAnimations = !currentAnimations.empty(); bool hadAnimations = !currentAnimations.empty();
initializeBattleAnimations(); initializeBattleAnimations();
stepFrameBattleAnimations(); tickFrameBattleAnimations(msPassed);
vstd::erase(currentAnimations, nullptr); vstd::erase(currentAnimations, nullptr);
if (hadAnimations && currentAnimations.empty()) if (hadAnimations && currentAnimations.empty())

View File

@ -91,9 +91,9 @@ class BattleStacksController
void removeExpiredColorFilters(); void removeExpiredColorFilters();
void initializeBattleAnimations(); void initializeBattleAnimations();
void stepFrameBattleAnimations(); void tickFrameBattleAnimations(uint32_t msPassed);
void updateBattleAnimations(); void updateBattleAnimations(uint32_t msPassed);
void updateHoveredStacks(); void updateHoveredStacks();
std::vector<const CStack *> selectHoveredStacks(); std::vector<const CStack *> selectHoveredStacks();
@ -138,7 +138,7 @@ public:
const CStack* getActiveStack() const; const CStack* getActiveStack() const;
const std::vector<uint32_t> getHoveredStacksUnitIds() const; const std::vector<uint32_t> getHoveredStacksUnitIds() const;
void update(); void tick(uint32_t msPassed);
/// returns position of animation needed to place stack in specific hex /// returns position of animation needed to place stack in specific hex
Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const; Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const;

View File

@ -14,6 +14,7 @@
#include "CIntObject.h" #include "CIntObject.h"
#include "CursorHandler.h" #include "CursorHandler.h"
#include "ShortcutHandler.h" #include "ShortcutHandler.h"
#include "FramerateManager.h"
#include "../CGameInfo.h" #include "../CGameInfo.h"
#include "../render/Colors.h" #include "../render/Colors.h"
@ -100,7 +101,7 @@ void CGuiHandler::init()
{ {
screenHandlerInstance = std::make_unique<ScreenHandler>(); screenHandlerInstance = std::make_unique<ScreenHandler>();
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>(); shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
mainFPSmng = new CFramerateManager(settings["video"]["targetfps"].Integer()); framerateManagerInstance = std::make_unique<FramerateManager>(settings["video"]["targetfps"].Integer());
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool(); isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float(); pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
@ -193,7 +194,7 @@ void CGuiHandler::totalRedraw()
void CGuiHandler::updateTime() void CGuiHandler::updateTime()
{ {
int ms = mainFPSmng->getElapsedMilliseconds(); int ms = framerateManager().getElapsedMilliseconds();
std::list<CIntObject*> hlp = timeinterested; std::list<CIntObject*> hlp = timeinterested;
for (auto & elem : hlp) for (auto & elem : hlp)
{ {
@ -692,10 +693,9 @@ void CGuiHandler::renderFrame()
disposed.clear(); disposed.clear();
} }
mainFPSmng->framerateDelay(); // holds a constant FPS framerateManager().framerateDelay(); // holds a constant FPS
} }
CGuiHandler::CGuiHandler() CGuiHandler::CGuiHandler()
: lastClick(-500, -500) : lastClick(-500, -500)
, lastClickTime(0) , lastClickTime(0)
@ -705,7 +705,6 @@ CGuiHandler::CGuiHandler()
, mouseButtonsMask(0) , mouseButtonsMask(0)
, continueEventHandling(true) , continueEventHandling(true)
, curInt(nullptr) , curInt(nullptr)
, mainFPSmng(nullptr)
, statusbar(nullptr) , statusbar(nullptr)
{ {
terminate_cond = new CondSh<bool>(false); terminate_cond = new CondSh<bool>(false);
@ -713,15 +712,21 @@ CGuiHandler::CGuiHandler()
CGuiHandler::~CGuiHandler() CGuiHandler::~CGuiHandler()
{ {
delete mainFPSmng;
delete terminate_cond; delete terminate_cond;
} }
ShortcutHandler & CGuiHandler::shortcutsHandler() ShortcutHandler & CGuiHandler::shortcutsHandler()
{ {
assert(shortcutsHandlerInstance);
return *shortcutsHandlerInstance; return *shortcutsHandlerInstance;
} }
FramerateManager & CGuiHandler::framerateManager()
{
assert(framerateManagerInstance);
return *framerateManagerInstance;
}
void CGuiHandler::moveCursorToPosition(const Point & position) void CGuiHandler::moveCursorToPosition(const Point & position)
{ {
SDL_WarpMouseInWindow(mainWindow, position.x, position.y); SDL_WarpMouseInWindow(mainWindow, position.x, position.y);
@ -783,7 +788,7 @@ void CGuiHandler::drawFPSCounter()
static SDL_Rect overlay = { 0, 0, 64, 32}; static SDL_Rect overlay = { 0, 0, 64, 32};
uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10); uint32_t black = SDL_MapRGB(screen->format, 10, 10, 10);
SDL_FillRect(screen, &overlay, black); SDL_FillRect(screen, &overlay, black);
std::string fps = std::to_string(mainFPSmng->getFramerate()); std::string fps = std::to_string(framerateManager().getFramerate());
graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10)); graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, Colors::YELLOW, Point(10, 10));
} }
@ -823,55 +828,3 @@ void CGuiHandler::onScreenResize()
totalRedraw(); totalRedraw();
} }
CFramerateManager::CFramerateManager(int newRate)
: rate(0)
, rateticks(0)
, fps(0)
, accumulatedFrames(0)
, accumulatedTime(0)
, lastticks(0)
, timeElapsed(0)
{
init(newRate);
}
void CFramerateManager::init(int newRate)
{
rate = newRate;
rateticks = 1000.0 / rate;
this->lastticks = SDL_GetTicks();
}
void CFramerateManager::framerateDelay()
{
ui32 currentTicks = SDL_GetTicks();
timeElapsed = currentTicks - lastticks;
accumulatedFrames++;
// FPS is higher than it should be, then wait some time
if(timeElapsed < rateticks)
{
int timeToSleep = (uint32_t)ceil(this->rateticks) - timeElapsed;
boost::this_thread::sleep(boost::posix_time::milliseconds(timeToSleep));
}
currentTicks = SDL_GetTicks();
// recalculate timeElapsed for external calls via getElapsed()
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
timeElapsed = std::min<ui32>(currentTicks - lastticks, 100);
lastticks = SDL_GetTicks();
accumulatedTime += timeElapsed;
if(accumulatedFrames >= 100)
{
//about 2 second should be passed
fps = static_cast<int>(ceil(1000.0 / (accumulatedTime / accumulatedFrames)));
accumulatedTime = 0;
accumulatedFrames = 0;
}
}

View File

@ -23,7 +23,7 @@ union SDL_Event;
struct SDL_MouseMotionEvent; struct SDL_MouseMotionEvent;
class ShortcutHandler; class ShortcutHandler;
class CFramerateManager; class FramerateManager;
class IStatusBar; class IStatusBar;
class CIntObject; class CIntObject;
class IUpdateable; class IUpdateable;
@ -44,32 +44,11 @@ enum class EUserEvent
FORCE_QUIT, //quit client without question FORCE_QUIT, //quit client without question
}; };
// A fps manager which holds game updates at a constant rate
class CFramerateManager
{
private:
double rateticks;
ui32 lastticks;
ui32 timeElapsed;
int rate;
int fps; // the actual fps value
ui32 accumulatedTime;
ui32 accumulatedFrames;
public:
CFramerateManager(int newRate); // initializes the manager with a given fps rate
void init(int newRate); // 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
ui32 getElapsedMilliseconds() const {return this->timeElapsed;}
ui32 getFrameNumber() const { return accumulatedFrames; }
ui32 getFramerate() const { return fps; };
};
// Handles GUI logic and drawing // Handles GUI logic and drawing
class CGuiHandler class CGuiHandler
{ {
public: public:
CFramerateManager * mainFPSmng; //to keep const framerate
std::list<std::shared_ptr<IShowActivatable>> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on) std::list<std::shared_ptr<IShowActivatable>> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
std::shared_ptr<IStatusBar> statusbar; std::shared_ptr<IStatusBar> statusbar;
@ -97,6 +76,7 @@ private:
CIntObjectList textInterested; CIntObjectList textInterested;
std::unique_ptr<IScreenHandler> screenHandlerInstance; std::unique_ptr<IScreenHandler> screenHandlerInstance;
std::unique_ptr<FramerateManager> framerateManagerInstance;
void handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed); void handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed);
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb); void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
@ -113,11 +93,15 @@ public:
public: public:
//objs to blit //objs to blit
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit; std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
/// returns current position of mouse cursor, relative to vcmi window /// returns current position of mouse cursor, relative to vcmi window
const Point & getCursorPosition() const; const Point & getCursorPosition() const;
ShortcutHandler & shortcutsHandler(); ShortcutHandler & shortcutsHandler();
FramerateManager & framerateManager();
/// Returns current logical screen dimensions
/// May not match size of window if user has UI scaling different from 100%
Point screenDimensions() const; Point screenDimensions() const;
/// returns true if at least one mouse button is pressed /// returns true if at least one mouse button is pressed

View File

@ -12,6 +12,7 @@
#include "CursorHandler.h" #include "CursorHandler.h"
#include "CGuiHandler.h" #include "CGuiHandler.h"
#include "FramerateManager.h"
#include "../renderSDL/CursorSoftware.h" #include "../renderSDL/CursorSoftware.h"
#include "../renderSDL/CursorHardware.h" #include "../renderSDL/CursorHardware.h"
#include "../render/CAnimation.h" #include "../render/CAnimation.h"
@ -250,7 +251,7 @@ void CursorHandler::updateSpellcastCursor()
{ {
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; frameTime += GH.framerateManager().getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame; size_t newFrame = frame;
while (frameTime >= frameDisplayDuration) while (frameTime >= frameDisplayDuration)

View File

@ -0,0 +1,57 @@
/*
* FramerateManager.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 "StdInc.h"
#include "FramerateManager.h"
FramerateManager::FramerateManager(int targetFrameRate)
: targetFrameTime(Duration(boost::chrono::seconds(1)) / targetFrameRate)
, lastFrameIndex(0)
, lastFrameTimes({})
, lastTimePoint (Clock::now())
{
boost::range::fill(lastFrameTimes, targetFrameTime);
}
void FramerateManager::framerateDelay()
{
Duration timeSpentBusy = Clock::now() - lastTimePoint;
// FPS is higher than it should be, then wait some time
if(timeSpentBusy < targetFrameTime)
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
// compute actual timeElapsed taking into account actual sleep interval
// limit it to 100 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
TimePoint currentTicks = Clock::now();
Duration timeElapsed = currentTicks - lastTimePoint;
if(timeElapsed > boost::chrono::milliseconds(100))
timeElapsed = boost::chrono::milliseconds(100);
lastTimePoint = currentTicks;
lastFrameIndex = (lastFrameIndex + 1) % lastFrameTimes.size();
lastFrameTimes[lastFrameIndex] = timeElapsed;
}
ui32 FramerateManager::getElapsedMilliseconds() const
{
return lastFrameTimes[lastFrameIndex] / boost::chrono::milliseconds(1);
}
ui32 FramerateManager::getFramerate() const
{
Duration accumulatedTime = std::accumulate(lastFrameTimes.begin(), lastFrameTimes.end(), Duration());
auto actualFrameTime = accumulatedTime / lastFrameTimes.size();
if(actualFrameTime == actualFrameTime.zero())
return 0;
return std::round(boost::chrono::duration<double>(1) / actualFrameTime);
};

View File

@ -0,0 +1,40 @@
/*
* FramerateManager.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
*
*/
#pragma once
/// Framerate manager controls current game frame rate by constantly trying to reach targeted frame rate
class FramerateManager
{
using Clock = boost::chrono::high_resolution_clock;
using TimePoint = Clock::time_point;
using Duration = Clock::duration;
/// cyclic buffer of durations of last frames
std::array<Duration, 60> lastFrameTimes;
Duration targetFrameTime;
TimePoint lastTimePoint;
/// index of last measured frome in lastFrameTimes array
ui32 lastFrameIndex;
public:
FramerateManager(int targetFramerate);
/// must be called every frame
/// updates framerate calculations and executes sleep to maintain target frame rate
void framerateDelay();
/// returns duration of last frame in seconds
ui32 getElapsedMilliseconds() const;
/// returns current estimation of frame rate
ui32 getFramerate() const;
};

View File

@ -55,6 +55,8 @@ BasicMapView::BasicMapView(const Point & offset, const Point & dimensions)
pos += offset; pos += offset;
pos.w = dimensions.x; pos.w = dimensions.x;
pos.h = dimensions.y; pos.h = dimensions.y;
addUsedEvents(TIME);
} }
void BasicMapView::render(Canvas & target, bool fullUpdate) void BasicMapView::render(Canvas & target, bool fullUpdate)
@ -64,21 +66,22 @@ void BasicMapView::render(Canvas & target, bool fullUpdate)
tilesCache->render(controller->getContext(), targetClipped, fullUpdate); tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
} }
void BasicMapView::tick(uint32_t msPassed)
{
controller->tick(msPassed);
}
void BasicMapView::show(SDL_Surface * to) void BasicMapView::show(SDL_Surface * to)
{ {
controller->updateBefore(GH.mainFPSmng->getElapsedMilliseconds());
Canvas target(to); Canvas target(to);
CSDL_Ext::CClipRectGuard guard(to, pos); CSDL_Ext::CClipRectGuard guard(to, pos);
render(target, false); render(target, false);
controller->updateAfter(GH.mainFPSmng->getElapsedMilliseconds()); controller->afterRender();
} }
void BasicMapView::showAll(SDL_Surface * to) void BasicMapView::showAll(SDL_Surface * to)
{ {
controller->updateBefore(0);
Canvas target(to); Canvas target(to);
CSDL_Ext::CClipRectGuard guard(to, pos); CSDL_Ext::CClipRectGuard guard(to, pos);
render(target, true); render(target, true);

View File

@ -37,6 +37,7 @@ public:
BasicMapView(const Point & offset, const Point & dimensions); BasicMapView(const Point & offset, const Point & dimensions);
~BasicMapView() override; ~BasicMapView() override;
void tick(uint32_t msPassed) override;
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;
}; };

View File

@ -89,7 +89,7 @@ std::shared_ptr<IMapRendererContext> MapViewController::getContext() const
return context; return context;
} }
void MapViewController::updateBefore(uint32_t timeDelta) void MapViewController::tick(uint32_t timeDelta)
{ {
// confirmed to match H3 for // confirmed to match H3 for
// - hero embarking on boat (500 ms) // - hero embarking on boat (500 ms)
@ -158,7 +158,7 @@ void MapViewController::updateBefore(uint32_t timeDelta)
} }
} }
void MapViewController::updateAfter(uint32_t timeDelta) void MapViewController::afterRender()
{ {
if(movementContext) if(movementContext)
{ {

View File

@ -83,8 +83,8 @@ public:
void setViewCenter(const int3 & position); void setViewCenter(const int3 & position);
void setViewCenter(const Point & position, int level); void setViewCenter(const Point & position, int level);
void setTileSize(const Point & tileSize); void setTileSize(const Point & tileSize);
void updateBefore(uint32_t timeDelta); void tick(uint32_t timePassed);
void updateAfter(uint32_t timeDelta); void afterRender();
void activateAdventureContext(uint32_t animationTime); void activateAdventureContext(uint32_t animationTime);
void activateAdventureContext(); void activateAdventureContext();

View File

@ -320,6 +320,8 @@ CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 fra
pos.h = anim->getImage(0, group)->height(); pos.h = anim->getImage(0, group)->height();
pos.x+= x; pos.x+= x;
pos.y+= y; pos.y+= y;
addUsedEvents(TIME);
} }
CShowableAnim::~CShowableAnim() CShowableAnim::~CShowableAnim()
@ -391,11 +393,14 @@ void CShowableAnim::show(SDL_Surface * to)
if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling
blitImage(first, group, to); blitImage(first, group, to);
blitImage(frame, group, to); blitImage(frame, group, to);
}
void CShowableAnim::tick(uint32_t msPassed)
{
if ((flags & PLAY_ONCE) && frame + 1 == last) if ((flags & PLAY_ONCE) && frame + 1 == last)
return; return;
frameTimePassed += GH.mainFPSmng->getElapsedMilliseconds(); frameTimePassed += msPassed;
if(frameTimePassed >= frameTimeTotal) if(frameTimePassed >= frameTimeTotal)
{ {

View File

@ -190,6 +190,7 @@ public:
//show current frame and increase counter //show current frame and increase counter
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;
void tick(uint32_t msPassed) override;
}; };
/// Creature-dependend animations like attacking, moving,... /// Creature-dependend animations like attacking, moving,...

View File

@ -68,7 +68,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town
area(nullptr), area(nullptr),
stateTimeCounter(BUILD_ANIMATION_FINISHED_TIMEPOINT) stateTimeCounter(BUILD_ANIMATION_FINISHED_TIMEPOINT)
{ {
addUsedEvents(LCLICK | RCLICK | HOVER); addUsedEvents(LCLICK | RCLICK | HOVER | TIME);
pos.x += str->pos.x; pos.x += str->pos.x;
pos.y += str->pos.y; pos.y += str->pos.y;
@ -163,16 +163,6 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
} }
} }
SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f)
{
SDL_Color ret;
ret.r = static_cast<uint8_t>(a.r * f + b.r * (1 - f));
ret.g = static_cast<uint8_t>(a.g * f + b.g * (1 - f));
ret.b = static_cast<uint8_t>(a.b * f + b.b * (1 - f));
ret.a = static_cast<uint8_t>(a.a * f + b.b * (1 - f));
return ret;
}
void CBuildingRect::show(SDL_Surface * to) void CBuildingRect::show(SDL_Surface * to)
{ {
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT; uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT;
@ -213,9 +203,12 @@ void CBuildingRect::show(SDL_Surface * to)
border->draw(to, pos.x, pos.y); border->draw(to, pos.x, pos.y);
} }
}
if(stateTimeCounter < BUILD_ANIMATION_FINISHED_TIMEPOINT) void CBuildingRect::tick(uint32_t msPassed)
stateTimeCounter += GH.mainFPSmng->getElapsedMilliseconds(); {
CShowableAnim::tick(msPassed);
stateTimeCounter += msPassed;
} }
void CBuildingRect::showAll(SDL_Surface * to) void CBuildingRect::showAll(SDL_Surface * to)

View File

@ -69,6 +69,7 @@ public:
void clickLeft(tribool down, bool previousState) override; void clickLeft(tribool down, bool previousState) override;
void clickRight(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override;
void mouseMoved (const Point & cursorPosition) override; void mouseMoved (const Point & cursorPosition) override;
void tick(uint32_t msPassed) override;
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;
}; };