aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/bladerunner/bladerunner.cpp26
-rw-r--r--engines/bladerunner/bladerunner.h5
-rw-r--r--engines/bladerunner/framelimiter.cpp130
-rw-r--r--engines/bladerunner/framelimiter.h91
-rw-r--r--engines/bladerunner/module.mk1
-rw-r--r--engines/bladerunner/outtake.cpp23
-rw-r--r--engines/bladerunner/outtake.h4
-rw-r--r--engines/bladerunner/ui/elevator.cpp58
-rw-r--r--engines/bladerunner/ui/elevator.h5
-rw-r--r--engines/bladerunner/ui/end_credits.cpp23
-rw-r--r--engines/bladerunner/ui/end_credits.h2
-rw-r--r--engines/bladerunner/ui/esper.cpp24
-rw-r--r--engines/bladerunner/ui/esper.h5
-rw-r--r--engines/bladerunner/ui/kia.cpp28
-rw-r--r--engines/bladerunner/ui/kia.h4
-rw-r--r--engines/bladerunner/ui/spinner.cpp64
-rw-r--r--engines/bladerunner/ui/spinner.h5
-rw-r--r--engines/bladerunner/ui/vk.cpp19
-rw-r--r--engines/bladerunner/ui/vk.h5
19 files changed, 379 insertions, 143 deletions
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 9262d4129f..b73afdd8b8 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -34,6 +34,7 @@
#include "bladerunner/crimes_database.h"
#include "bladerunner/debugger.h"
#include "bladerunner/dialogue_menu.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/font.h"
#include "bladerunner/game_flags.h"
#include "bladerunner/game_info.h"
@@ -216,6 +217,8 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
_actors[i] = nullptr;
}
_debugger = nullptr;
+ _mainLoopFrameLimiter = nullptr;
+
walkingReset();
_actorUpdateCounter = 0;
@@ -526,6 +529,8 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
_cosTable1024 = new Common::CosineTable(1024); // 10-bits = 1024 points for 2*PI;
_sinTable1024 = new Common::SineTable(1024);
+ _mainLoopFrameLimiter = new Framelimiter(this, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
+
_view = new View();
_sceneObjects = new SceneObjects(this, _view);
@@ -927,6 +932,11 @@ void BladeRunnerEngine::shutdown() {
delete _screenEffects;
_screenEffects = nullptr;
+
+ if (_mainLoopFrameLimiter) {
+ delete _mainLoopFrameLimiter;
+ _mainLoopFrameLimiter = nullptr;
+ }
}
bool BladeRunnerEngine::loadSplash() {
@@ -955,7 +965,7 @@ bool BladeRunnerEngine::isMouseButtonDown() const {
void BladeRunnerEngine::gameLoop() {
_gameIsRunning = true;
- _timeOfMainGameLoopTickPrevious = _time->currentSystem();
+ _mainLoopFrameLimiter->init();
do {
if (_playerDead) {
playerDied();
@@ -970,7 +980,7 @@ void BladeRunnerEngine::gameTick() {
handleEvents();
if (!_gameIsRunning || !_windowIsActive) {
- _timeOfMainGameLoopTickPrevious = _time->currentSystem();
+ _mainLoopFrameLimiter->init();
return;
}
@@ -979,7 +989,7 @@ void BladeRunnerEngine::gameTick() {
Common::Error runtimeError = Common::Error(Common::kUnknownError, _("A required game resource was not found"));
GUI::MessageDialog dialog(runtimeError.getDesc());
dialog.runModal();
- _timeOfMainGameLoopTickPrevious = _time->currentSystem();
+ _mainLoopFrameLimiter->init();
return;
}
}
@@ -1111,15 +1121,13 @@ void BladeRunnerEngine::gameTick() {
_subtitles->tick(_surfaceFront);
- uint32 mainGameLoopTickNow = _time->currentSystem();
- if (mainGameLoopTickNow - _timeOfMainGameLoopTickPrevious < kUpdateFrameTimeInMs) {
- return;
- }
- _timeOfMainGameLoopTickPrevious = mainGameLoopTickNow;
// Without this condition the game may flash back to the game screen
// between and ending outtake and the end credits.
if (!_gameOver) {
- blitToScreen(_surfaceFront);
+ if (_mainLoopFrameLimiter->shouldExecuteScreenUpdate()) {
+ blitToScreen(_surfaceFront);
+ _mainLoopFrameLimiter->postScreenUpdate();
+ }
}
}
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index 8aee6b6416..ebc8bd06b2 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -73,6 +73,7 @@ class DialogueMenu;
class Elevator;
class EndCredits;
class ESPER;
+class Framelimiter;
class Font;
class GameFlags;
class GameInfo;
@@ -119,8 +120,6 @@ public:
// 2: all time code uses uint32 (since July 17 2019),
static const int kBladeRunnerScummVMVersion = 2;
- static const uint32 kUpdateFrameTimeInMs = 16u;
-
bool _gameIsRunning;
bool _windowIsActive;
int _playerLosesControlCounter;
@@ -198,6 +197,8 @@ public:
Common::CosineTable *_cosTable1024;
Common::SineTable *_sinTable1024;
+ Framelimiter *_mainLoopFrameLimiter;
+
bool _isWalkingInterruptible;
bool _interruptWalking;
bool _playerActorIdle;
diff --git a/engines/bladerunner/framelimiter.cpp b/engines/bladerunner/framelimiter.cpp
new file mode 100644
index 0000000000..08cc9fc99b
--- /dev/null
+++ b/engines/bladerunner/framelimiter.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "bladerunner/framelimiter.h"
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/time.h"
+#include "common/system.h"
+
+namespace BladeRunner {
+
+Framelimiter::Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs) {
+ _vm = vm;
+
+// // FUTURE: The frame limiter is disabled when vsync is enabled.
+// _enabled = !_system->getFeatureState(OSystem::kFeatureVSync);
+ _enabled = true;
+ _useDelayMs = useDelayMs;
+
+ _speedLimitMs = 0u;
+ uint32 framerate = 1u; // dummy init
+ switch (framerateMode) {
+ case kFramelimiter15fps:
+ framerate = 15u;
+ break;
+ case kFramelimiter25fps:
+ framerate = 25u;
+ break;
+ case kFramelimiter30fps:
+ framerate = 30u;
+ break;
+ case kFramelimiter60fps:
+ framerate = 60u;
+ break;
+ case kFramelimiter120fps:
+ framerate = 120u;
+ break;
+ case kFramelimiterDisabled:
+ // fall through
+ default:
+ _enabled = false;
+ break;
+ }
+
+ if (_enabled) {
+ _speedLimitMs = 1000 / CLIP<uint32>(framerate, 1, 120);
+ }
+
+ reset();
+}
+
+Framelimiter::~Framelimiter() { }
+
+void Framelimiter::init(bool forceFirstPass) {
+ reset();
+ _timeOfLastPass = _vm->_time->currentSystem();
+ _forceFirstPass = forceFirstPass;
+}
+
+uint32 Framelimiter::getLastFrameDuration() const {
+ return _lastFrameDurationMs;
+}
+
+uint32 Framelimiter::getTimeOfCurrentPass() const {
+ return _timeOfCurrentPass;
+}
+
+uint32 Framelimiter::getTimeOfLastPass() const {
+ return _timeOfLastPass;
+}
+
+bool Framelimiter::shouldExecuteScreenUpdate() {
+ bool shouldUpdateScreen = true;
+ _timeOfCurrentPass = _vm->_time->currentSystem();
+ if (_enabled) {
+ shouldUpdateScreen = ((_timeOfCurrentPass - _timeOfLastPass) >= _speedLimitMs) || _forceFirstPass;
+
+ if (shouldUpdateScreen) {
+ if (_forceFirstPass) {
+ _forceFirstPass = false;
+ }
+ _startFrameTime = _timeOfCurrentPass;
+ }
+ }
+ return shouldUpdateScreen;
+}
+
+void Framelimiter::postScreenUpdate() {
+ _timeOfLastPass = _timeOfCurrentPass;
+ if (_enabled) {
+
+ if (_useDelayMs) {
+ uint32 endFrameTime = _vm->_time->currentSystem();
+ uint32 frameDuration = endFrameTime - _startFrameTime;
+
+ if (frameDuration < _speedLimitMs) {
+ _vm->_system->delayMillis(_speedLimitMs - frameDuration);
+ }
+ }
+ }
+}
+
+void Framelimiter::reset() {
+ _forceFirstPass = false;
+ _timeOfLastPass = 0u;
+ _timeOfCurrentPass = 0u;
+ _startFrameTime = 0u;
+ _lastFrameDurationMs = _speedLimitMs;
+}
+
+} // End of namespace BladeRunner
diff --git a/engines/bladerunner/framelimiter.h b/engines/bladerunner/framelimiter.h
new file mode 100644
index 0000000000..66ee0f3b38
--- /dev/null
+++ b/engines/bladerunner/framelimiter.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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
+ * of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BLADERUNNER_FRAMELIMITER_H
+#define BLADERUNNER_FRAMELIMITER_H
+
+#include "bladerunner/bladerunner.h"
+
+namespace BladeRunner {
+
+enum FramelimiterFpsRate {
+ kFramelimiterDisabled = 0,
+ kFramelimiter15fps = 1,
+ kFramelimiter25fps = 2,
+ kFramelimiter30fps = 3,
+ kFramelimiter60fps = 4,
+ kFramelimiter120fps = 5
+};
+
+class BladeRunnerEngine;
+
+class Framelimiter {
+ friend class Debugger;
+
+public:
+ static const FramelimiterFpsRate kDefaultFpsRate = kFramelimiter60fps;
+ static const bool kDefaultUseDelayMillis = false;
+
+private:
+ BladeRunnerEngine *_vm;
+
+ bool _forceFirstPass;
+ uint32 _speedLimitMs;
+
+ // A pass is when a tick or while loop that contains a potential screen update is repeated
+ // it's essentially when the check is made for a screen update
+ // Not every pass will necessarily result in a screen update (because that's the purpose of the frame limiter)
+ // So the "_startFrameTime" is not always equal to "_timeOfCurrentPass"
+ uint32 _timeOfLastPass;
+ uint32 _timeOfCurrentPass;
+
+ uint32 _startFrameTime; // is updated and valid, only if the current pass will result in a screen update (see method: shouldExecuteScreenUpdate())
+ uint32 _lastFrameDurationMs; // can be used for average FPS calculation and display purposes when frame limiter is enabled
+
+ bool _enabled;
+ bool _useDelayMs; // true: will use calls to delayMillis(), false: will use non-blocking software timer instead
+
+public:
+ Framelimiter(BladeRunnerEngine *vm, FramelimiterFpsRate framerateMode, bool useDelayMs);
+ ~Framelimiter();
+
+// void startFrame();
+// void delayBeforeSwap();
+
+// void pause(bool pause);
+
+ void init(bool forceFirstPass = true);
+ uint32 getLastFrameDuration() const;
+ uint32 getTimeOfCurrentPass() const;
+ uint32 getTimeOfLastPass() const;
+
+ bool shouldExecuteScreenUpdate();
+ void postScreenUpdate();
+
+private:
+ void reset();
+
+};
+
+} // End of namespace BladeRunner
+
+#endif
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index e52f8e5bb1..6be142d441 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS = \
decompress_lzo.o \
detection.o \
dialogue_menu.o \
+ framelimiter.o \
fog.o \
font.o \
game_flags.o \
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index ba02bda21e..2f49099455 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -24,6 +24,7 @@
#include "bladerunner/bladerunner.h"
#include "bladerunner/chapters.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/subtitles.h"
#include "bladerunner/vqa_player.h"
#include "bladerunner/time.h"
@@ -37,10 +38,16 @@ namespace BladeRunner {
OuttakePlayer::OuttakePlayer(BladeRunnerEngine *vm) {
_vm = vm;
_surfaceVideo.create(_vm->_surfaceBack.w, _vm->_surfaceBack.h, _vm->_surfaceBack.format);
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
OuttakePlayer::~OuttakePlayer() {
_surfaceVideo.free();
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void OuttakePlayer::play(const Common::String &name, bool noLocalization, int container) {
@@ -70,28 +77,20 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
_vm->_vqaIsPlaying = true;
_vm->_vqaStopIsRequested = false;
- uint32 timeNow = 0;
- bool firstFrame = true;
- _timeLast = _vm->_time->currentSystem();
+ _framelimiter->init();
while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
_vm->handleEvents();
if (!_vm->_windowIsActive) {
- _timeLast = _vm->_time->currentSystem();
+ _framelimiter->init();
continue;
}
- timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !firstFrame) {
+ if (!_framelimiter->shouldExecuteScreenUpdate()) {
continue;
}
- if (firstFrame) {
- firstFrame = false;
- }
-
int frame = vqaPlayer.update();
blit(_surfaceVideo, _vm->_surfaceFront); // This helps to make subtitles disappear properly, if the video is rendered in separate surface and then pushed to the front surface
if (frame == -3) { // end of video
@@ -103,7 +102,7 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
_vm->_subtitles->tickOuttakes(_vm->_surfaceFront);
_vm->blitToScreen(_vm->_surfaceFront);
}
- _timeLast = timeNow;
+ _framelimiter->postScreenUpdate();
}
_vm->_vqaIsPlaying = false;
diff --git a/engines/bladerunner/outtake.h b/engines/bladerunner/outtake.h
index fc202736fa..0305e80554 100644
--- a/engines/bladerunner/outtake.h
+++ b/engines/bladerunner/outtake.h
@@ -30,11 +30,13 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class OuttakePlayer {
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
+
Graphics::Surface _surfaceVideo;
- uint32 _timeLast;
public:
OuttakePlayer(BladeRunnerEngine *vm);
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index db3a465ee4..d01acc21ed 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -25,6 +25,7 @@
#include "bladerunner/actor.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/audio_player.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/game_info.h"
#include "bladerunner/mouse.h"
#include "bladerunner/shape.h"
@@ -44,11 +45,17 @@ Elevator::Elevator(BladeRunnerEngine *vm) {
_vm = vm;
reset();
_imagePicker = new UIImagePicker(vm, 8);
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
Elevator::~Elevator() {
delete _imagePicker;
_imagePicker = nullptr;
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
int Elevator::activate(int elevatorId) {
@@ -187,8 +194,7 @@ int Elevator::activate(int elevatorId) {
void Elevator::open() {
resetDescription();
_isOpen = true;
- _timeLast = _vm->_time->currentSystem();
- _firstTickCall = true;
+ _framelimiter->init();
}
bool Elevator::isOpen() const {
@@ -207,44 +213,36 @@ int Elevator::handleMouseDown(int x, int y) {
void Elevator::tick() {
if (!_vm->_windowIsActive) {
- _timeLast = _vm->_time->currentSystem();
+ _framelimiter->init();
return;
}
- uint32 timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !_firstTickCall) {
- return;
- }
+ if (_framelimiter->shouldExecuteScreenUpdate()) {
+ int frame = _vqaPlayer->update();
+ assert(frame >= -1);
- if (_firstTickCall) {
- _firstTickCall = false;
- }
+ // vqaPlayer renders to _surfaceBack
+ blit(_vm->_surfaceBack, _vm->_surfaceFront);
- int frame = _vqaPlayer->update();
- assert(frame >= -1);
+ Common::Point p = _vm->getMousePos();
- // vqaPlayer renders to _surfaceBack
- blit(_vm->_surfaceBack, _vm->_surfaceFront);
+ // TODO(madmoose): BLADE.EXE has hasHoveredImage before handleMouseAction?
+ _imagePicker->handleMouseAction(p.x, p.y, false, false, false);
+ if (_imagePicker->hasHoveredImage()) {
+ _vm->_mouse->setCursor(1);
+ } else {
+ _vm->_mouse->setCursor(0);
+ }
- Common::Point p = _vm->getMousePos();
+ _imagePicker->draw(_vm->_surfaceFront);
+ _vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
- // TODO(madmoose): BLADE.EXE has hasHoveredImage before handleMouseAction?
- _imagePicker->handleMouseAction(p.x, p.y, false, false, false);
- if (_imagePicker->hasHoveredImage()) {
- _vm->_mouse->setCursor(1);
- } else {
- _vm->_mouse->setCursor(0);
- }
-
- _imagePicker->draw(_vm->_surfaceFront);
- _vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
+ _vm->_subtitles->tick(_vm->_surfaceFront);
- _vm->_subtitles->tick(_vm->_surfaceFront);
-
- _vm->blitToScreen(_vm->_surfaceFront);
+ _vm->blitToScreen(_vm->_surfaceFront);
+ _framelimiter->postScreenUpdate();
+ }
tickDescription();
- _timeLast = timeNow;
}
void Elevator::buttonClick(int buttonId) {
diff --git a/engines/bladerunner/ui/elevator.h b/engines/bladerunner/ui/elevator.h
index 92afb6e2b7..c64152c3fc 100644
--- a/engines/bladerunner/ui/elevator.h
+++ b/engines/bladerunner/ui/elevator.h
@@ -28,12 +28,14 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class Shape;
class VQAPlayer;
class UIImagePicker;
class Elevator {
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
bool _isOpen;
VQAPlayer *_vqaPlayer;
int _buttonClicked;
@@ -43,9 +45,6 @@ class Elevator {
int _sentenceId;
uint32 _timeSpeakDescriptionStart;
- uint32 _timeLast;
- bool _firstTickCall;
-
public:
Elevator(BladeRunnerEngine *vm);
~Elevator();
diff --git a/engines/bladerunner/ui/end_credits.cpp b/engines/bladerunner/ui/end_credits.cpp
index 9ff6e1dbce..291014c5a3 100644
--- a/engines/bladerunner/ui/end_credits.cpp
+++ b/engines/bladerunner/ui/end_credits.cpp
@@ -29,6 +29,7 @@
#include "bladerunner/game_constants.h"
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/audio_speech.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/mouse.h"
@@ -41,9 +42,14 @@ namespace BladeRunner {
EndCredits::EndCredits(BladeRunnerEngine *vm) {
_vm = vm;
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
EndCredits::~EndCredits() {
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void EndCredits::show() {
@@ -92,8 +98,7 @@ void EndCredits::show() {
_vm->_vqaStopIsRequested = false;
double position = 0.0;
- uint32 timeLast = _vm->_time->currentSystem();
- bool firstPass = true;
+ _framelimiter->init();
while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
if (position >= textPositions[textCount - 1]) {
@@ -104,22 +109,15 @@ void EndCredits::show() {
_vm->handleEvents();
if (!_vm->_windowIsActive) {
- timeLast = _vm->_time->currentSystem();
-
+ _framelimiter->init();
continue;
}
- uint32 timeNow = _vm->_time->currentSystem();
- if (timeNow - timeLast < _vm->kUpdateFrameTimeInMs && !firstPass) {
+ if (!_framelimiter->shouldExecuteScreenUpdate()) {
continue;
}
- if (firstPass) {
- firstPass = false;
- }
-
- position += (double)(timeNow - timeLast) * 0.05f; // unsigned difference is intentional
- timeLast = timeNow;
+ position += (double)(_framelimiter->getTimeOfCurrentPass() - _framelimiter->getTimeOfLastPass()) * 0.05f; // unsigned difference is intentional
_vm->_surfaceFront.fillRect(Common::Rect(640, 480), 0);
@@ -156,6 +154,7 @@ void EndCredits::show() {
_vm->_surfaceFront.fillRect(Common::Rect(0, 452, 640, 480), 0);
_vm->blitToScreen(_vm->_surfaceFront);
+ _framelimiter->postScreenUpdate();
}
_vm->_vqaIsPlaying = false;
diff --git a/engines/bladerunner/ui/end_credits.h b/engines/bladerunner/ui/end_credits.h
index 39086cd796..6c45adfd60 100644
--- a/engines/bladerunner/ui/end_credits.h
+++ b/engines/bladerunner/ui/end_credits.h
@@ -26,9 +26,11 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class EndCredits {
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
public:
EndCredits(BladeRunnerEngine *vm);
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
index 5f49830f44..f5257bf694 100644
--- a/engines/bladerunner/ui/esper.cpp
+++ b/engines/bladerunner/ui/esper.cpp
@@ -28,6 +28,7 @@
#include "bladerunner/bladerunner.h"
#include "bladerunner/debugger.h"
#include "bladerunner/decompress_lcw.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/font.h"
#include "bladerunner/game_info.h"
#include "bladerunner/mouse.h"
@@ -57,7 +58,6 @@ ESPER::ESPER(BladeRunnerEngine *vm) {
_isDrawingSelection = false;
_isOpen = false;
- _firstTickCall = false;
_shapeButton = nullptr;
_shapeThumbnail = nullptr;
_vqaPlayerMain = nullptr;
@@ -67,11 +67,17 @@ ESPER::ESPER(BladeRunnerEngine *vm) {
reset();
_buttons = new UIImagePicker(vm, kPhotoCount + 4);
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
ESPER::~ESPER() {
delete _buttons;
reset();
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void ESPER::open(Graphics::Surface *surface) {
@@ -116,8 +122,8 @@ void ESPER::open(Graphics::Surface *surface) {
_vqaPlayerMain->setLoop(2, -1, kLoopSetModeJustStart, nullptr, nullptr);
_isOpen = true;
- _timeLast = _vm->_time->currentSystem();
- _firstTickCall = true;
+ _framelimiter->init();
+
_flash = false;
_script = new ESPERScript(_vm);
@@ -210,16 +216,11 @@ void ESPER::handleMouseDown(int x, int y, bool mainButton) {
void ESPER::tick() {
if (!_vm->_windowIsActive) {
- _timeLast = _vm->_time->currentSystem();
+ _framelimiter->init();
return;
}
- uint32 timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- if (timeNow - _timeLast >= _vm->kUpdateFrameTimeInMs || _firstTickCall) {
- if (_firstTickCall) {
- _firstTickCall = false;
- }
+ if (_framelimiter->shouldExecuteScreenUpdate()) {
tickSound();
blit(_vm->_surfaceBack, _vm->_surfaceFront);
@@ -242,8 +243,7 @@ void ESPER::tick() {
_vm->_subtitles->tick(_vm->_surfaceFront);
_vm->blitToScreen(_vm->_surfaceFront);
- // TODO: implement 60hz lock for smoother experience
- _timeLast = timeNow;
+ _framelimiter->postScreenUpdate();
}
if (_statePhoto == kEsperPhotoStateVideoShow) {
diff --git a/engines/bladerunner/ui/esper.h b/engines/bladerunner/ui/esper.h
index 6feb2cae2d..818d51ca44 100644
--- a/engines/bladerunner/ui/esper.h
+++ b/engines/bladerunner/ui/esper.h
@@ -31,6 +31,7 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class Font;
class Shape;
class VQAPlayer;
@@ -87,6 +88,7 @@ class ESPER {
};
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
ESPERScript *_script;
bool _isWaiting;
@@ -184,9 +186,6 @@ class ESPER {
int _volume3;
int _ambientVolume;
- uint32 _timeLast;
- bool _firstTickCall;
-
public:
ESPER(BladeRunnerEngine *vm);
~ESPER();
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index a40a78670e..501a1e7cf2 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -26,6 +26,7 @@
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/combat.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/font.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/game_flags.h"
@@ -80,7 +81,6 @@ KIA::KIA(BladeRunnerEngine *vm) {
_playerPhotograph = nullptr;
_playerSliceModelId = -1;
_playerSliceModelAngle = 0.0f;
- _timeLast = _vm->_time->currentSystem();
_playerActorDialogueQueuePosition = 0;
_playerActorDialogueQueueSize = 0;
_playerActorDialogueState = 0;
@@ -93,7 +93,7 @@ KIA::KIA(BladeRunnerEngine *vm) {
// original imageCount was 22. We add +1 to have a description box for objects in cut content
// We don't have separated cases here, for _vm->_cutContent since that causes assertion fault if
- // loading a "restoed content" save game in a "original game" version
+ // loading a "restored content" save game in a "original game" version
_buttons = new UIImagePicker(_vm, 23);
_crimesSection = new KIASectionCrimes(_vm, _vm->_playerActor->_clues);
@@ -110,6 +110,9 @@ KIA::KIA(BladeRunnerEngine *vm) {
_playerActorDialogueQueue[i].actorId = -1;
_playerActorDialogueQueue[i].sentenceId = -1;
}
+
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
+ _framelimiter->init();
}
KIA::~KIA() {
@@ -133,6 +136,11 @@ KIA::~KIA() {
delete _shapes;
delete _log;
delete _script;
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void KIA::reset() {
@@ -227,18 +235,17 @@ bool KIA::isOpen() const {
void KIA::tick() {
if (!isOpen()) {
- _timeLast = _vm->_time->currentSystem();
return;
}
- uint32 timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- uint32 timeDiff = timeNow - _timeLast;
-
- if (timeDiff < _vm->kUpdateFrameTimeInMs) {
+ if (!_framelimiter->shouldExecuteScreenUpdate()) {
return;
}
+ uint32 timeNow = _framelimiter->getTimeOfCurrentPass();
+ // unsigned difference is intentional
+ uint32 timeDiff = timeNow - _framelimiter->getTimeOfLastPass();
+
if (_playerActorDialogueQueueSize == _playerActorDialogueQueuePosition) {
_playerActorDialogueState = 0;
} else if (_playerActorDialogueState == 0) {
@@ -389,7 +396,7 @@ void KIA::tick() {
_vm->blitToScreen(_vm->_surfaceFront);
- _timeLast = timeNow;
+ _framelimiter->postScreenUpdate();
}
void KIA::resume() {
@@ -684,7 +691,8 @@ void KIA::init() {
playerReset();
_playerVqaFrame = 0;
_playerVqaTimeLast = _vm->_time->currentSystem();
- _timeLast = _vm->_time->currentSystem();
+
+ _framelimiter->init();
if (_vm->_gameFlags->query(kFlagKIAPrivacyAddon) && !_vm->_gameFlags->query(kFlagKIAPrivacyAddonIntro)) {
_vm->_gameFlags->set(kFlagKIAPrivacyAddonIntro);
diff --git a/engines/bladerunner/ui/kia.h b/engines/bladerunner/ui/kia.h
index 2a87f5df6a..fdc4d8ef15 100644
--- a/engines/bladerunner/ui/kia.h
+++ b/engines/bladerunner/ui/kia.h
@@ -34,6 +34,7 @@ struct KeyState;
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class KIALog;
class KIAScript;
class KIASectionBase;
@@ -75,6 +76,7 @@ class KIA {
};
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
int _transitionId;
@@ -87,7 +89,7 @@ class KIA {
int _playerSliceModelId;
float _playerSliceModelAngle;
Graphics::Surface _playerImage;
- uint32 _timeLast;
+// uint32 _timeLast;
ActorDialogueQueueEntry _playerActorDialogueQueue[kPlayerActorDialogueQueueCapacity];
int _playerActorDialogueQueuePosition;
diff --git a/engines/bladerunner/ui/spinner.cpp b/engines/bladerunner/ui/spinner.cpp
index 2b084c9a2c..85d2c373bb 100644
--- a/engines/bladerunner/ui/spinner.cpp
+++ b/engines/bladerunner/ui/spinner.cpp
@@ -28,6 +28,7 @@
#include "bladerunner/ambient_sounds.h"
#include "bladerunner/game_info.h"
#include "bladerunner/subtitles.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/mouse.h"
#include "bladerunner/savefile.h"
@@ -48,6 +49,7 @@ Spinner::Spinner(BladeRunnerEngine *vm) {
reset();
_imagePicker = new UIImagePicker(vm, kSpinnerDestinations);
_vqaPlayer = nullptr;
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
Spinner::~Spinner() {
@@ -59,6 +61,11 @@ Spinner::~Spinner() {
_vqaPlayer->close();
delete _vqaPlayer;
}
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void Spinner::setSelectableDestinationFlag(int destination, bool selectable) {
@@ -79,9 +86,8 @@ int Spinner::chooseDestination(int loopId, bool immediately) {
}
if (loopId < 0) {
- _isOpen = true;
- _timeLast = _vm->_time->currentSystem();
- _firstTickCall = true;
+ // call Spinner:open()
+ open();
} else {
_vm->playerLosesControl();
_vm->_scene->loopStartSpecial(kSceneLoopModeSpinner, loopId, immediately);
@@ -231,8 +237,7 @@ void Spinner::mouseUpCallback(int destinationImage, void *self) {
void Spinner::open() {
_isOpen = true;
- _timeLast = _vm->_time->currentSystem();
- _firstTickCall = true;
+ _framelimiter->init();
}
bool Spinner::isOpen() const {
@@ -251,45 +256,39 @@ int Spinner::handleMouseDown(int x, int y) {
void Spinner::tick() {
if (!_vm->_windowIsActive) {
- _timeLast = _vm->_time->currentSystem();
+ _framelimiter->init();
return;
}
- uint32 timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- if (timeNow - _timeLast < _vm->kUpdateFrameTimeInMs && !_firstTickCall) {
- return;
- }
+ if (_framelimiter->shouldExecuteScreenUpdate()) {
+ int frame = _vqaPlayer->update();
+ assert(frame >= -1);
- if (_firstTickCall) {
- _firstTickCall = false;
- }
+ // vqaPlayer renders to _surfaceBack
+ blit(_vm->_surfaceBack, _vm->_surfaceFront);
- int frame = _vqaPlayer->update();
- assert(frame >= -1);
+ Common::Point p = _vm->getMousePos();
+ _imagePicker->handleMouseAction(p.x, p.y, false, false, false);
+ if (_imagePicker->hasHoveredImage()) {
+ _vm->_mouse->setCursor(1);
+ } else {
+ _vm->_mouse->setCursor(0);
+ }
+ _imagePicker->draw(_vm->_surfaceFront);
+ _vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
+ _imagePicker->drawTooltip(_vm->_surfaceFront, p.x, p.y);
- // vqaPlayer renders to _surfaceBack
- blit(_vm->_surfaceBack, _vm->_surfaceFront);
+ if (_vm->_cutContent) {
+ _vm->_subtitles->tick(_vm->_surfaceFront);
+ }
+ _vm->blitToScreen(_vm->_surfaceFront);
+ _framelimiter->postScreenUpdate();
- Common::Point p = _vm->getMousePos();
- _imagePicker->handleMouseAction(p.x, p.y, false, false, false);
- if (_imagePicker->hasHoveredImage()) {
- _vm->_mouse->setCursor(1);
- } else {
- _vm->_mouse->setCursor(0);
}
- _imagePicker->draw(_vm->_surfaceFront);
- _vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
- _imagePicker->drawTooltip(_vm->_surfaceFront, p.x, p.y);
if (_vm->_cutContent) {
- _vm->_subtitles->tick(_vm->_surfaceFront);
- }
- _vm->blitToScreen(_vm->_surfaceFront);
- if (_vm->_cutContent) {
tickDescription();
}
- _timeLast = timeNow;
}
void Spinner::setSelectedDestination(int destination) {
@@ -302,7 +301,6 @@ void Spinner::reset() {
}
_isOpen = false;
- _firstTickCall = false;
_destinations = nullptr;
_selectedDestination = -1;
_imagePicker = nullptr;
diff --git a/engines/bladerunner/ui/spinner.h b/engines/bladerunner/ui/spinner.h
index 0477595ef4..78c2943eb2 100644
--- a/engines/bladerunner/ui/spinner.h
+++ b/engines/bladerunner/ui/spinner.h
@@ -29,6 +29,7 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class SaveFileReadStream;
class SaveFileWriteStream;
class Shape;
@@ -44,6 +45,7 @@ class Spinner {
};
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
bool _isDestinationSelectable[kSpinnerDestinations];
bool _isOpen;
VQAPlayer *_vqaPlayer;
@@ -56,9 +58,6 @@ class Spinner {
int _sentenceId;
uint32 _timeSpeakDescriptionStart;
- uint32 _timeLast;
- bool _firstTickCall;
-
public:
Spinner(BladeRunnerEngine *vm);
~Spinner();
diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp
index 06056c33dd..7e24b798b6 100644
--- a/engines/bladerunner/ui/vk.cpp
+++ b/engines/bladerunner/ui/vk.cpp
@@ -27,6 +27,7 @@
#include "bladerunner/audio_player.h"
#include "bladerunner/bladerunner.h"
#include "bladerunner/combat.h"
+#include "bladerunner/framelimiter.h"
#include "bladerunner/font.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/game_flags.h"
@@ -53,10 +54,16 @@ VK::VK(BladeRunnerEngine *vm) {
_vm = vm;
reset();
+ _framelimiter = new Framelimiter(_vm, Framelimiter::kDefaultFpsRate, Framelimiter::kDefaultUseDelayMillis);
}
VK::~VK() {
reset();
+
+ if (_framelimiter) {
+ delete _framelimiter;
+ _framelimiter = nullptr;
+ }
}
void VK::open(int actorId, int calibrationRatio) {
@@ -126,8 +133,7 @@ void VK::open(int actorId, int calibrationRatio) {
}
_isOpen = true;
- _timeLast = _vm->_time->currentSystem();
- _firstTickCall = true;
+ _framelimiter->init();
_script = new VKScript(_vm);
@@ -193,12 +199,7 @@ void VK::close() {
void VK::tick() {
- uint32 timeNow = _vm->_time->currentSystem();
- // unsigned difference is intentional
- if (timeNow - _timeLast >= _vm->kUpdateFrameTimeInMs || _firstTickCall) {
- if (_firstTickCall) {
- _firstTickCall = false;
- }
+ if (_framelimiter->shouldExecuteScreenUpdate()) {
int mouseX, mouseY;
_vm->_mouse->getXY(&mouseX, &mouseY);
if (!_vm->_mouse->isDisabled()) {
@@ -220,7 +221,7 @@ void VK::tick() {
_vm->_subtitles->tick(_vm->_surfaceFront);
_vm->blitToScreen(_vm->_surfaceFront);
- _timeLast = timeNow;
+ _framelimiter->postScreenUpdate();
}
// unsigned difference is intentional
diff --git a/engines/bladerunner/ui/vk.h b/engines/bladerunner/ui/vk.h
index 8593e6b1b3..a8802046e3 100644
--- a/engines/bladerunner/ui/vk.h
+++ b/engines/bladerunner/ui/vk.h
@@ -31,6 +31,7 @@
namespace BladeRunner {
class BladeRunnerEngine;
+class Framelimiter;
class VKScript;
class Shape;
class UIImagePicker;
@@ -47,6 +48,7 @@ class VK {
};
BladeRunnerEngine *_vm;
+ Framelimiter *_framelimiter;
VKScript *_script;
@@ -124,9 +126,6 @@ class VK {
uint32 _timeNextEyeLineStepStart;
uint32 _timeNextEyeLineStart;
- uint32 _timeLast;
- bool _firstTickCall;
-
public:
VK(BladeRunnerEngine *vm);
~VK();