DIRECTOR: Add CD loading delay quirk

Spaceship Warlock is optimised for very slow disk reads; quite a lot of
movie changes will be preceded by a music cue and a still image, knowing
that the system will take a couple of seconds to read the next file.

As a compromise, add a fake delay of [file size]*1000/150000 ms to movie
switches. This can be short-circuited by clicking the mouse, so it is
still possible to navigate around quickly.
This commit is contained in:
Scott Percival 2024-02-02 22:12:22 +08:00
parent d3ffe66fe5
commit b163dba86d
6 changed files with 54 additions and 4 deletions

View File

@ -101,6 +101,8 @@ DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gam
_forceDate.tm_mon = -1;
_forceDate.tm_year = -1;
_forceDate.tm_wday = -1;
_loadSlowdownFactor = 0;
_loadSlowdownCooldownTime = 0;
_wm = nullptr;

View File

@ -228,12 +228,13 @@ public:
bool desktopEnabled();
// events.cpp
bool processEvents(bool captureClick = false);
bool processEvents(bool captureClick = false, bool skipWindowManager = false);
void processEventQUIT();
uint32 getMacTicks();
// game-quirks.cpp
void gameQuirks(const char *target, Common::Platform platform);
void loadSlowdownCooloff(uint32 delay = 2000);
void delayMillis(uint32 delay);
@ -282,6 +283,8 @@ public:
// used for quirks
byte _fpsLimit;
TimeDate _forceDate;
uint32 _loadSlowdownFactor;
uint32 _loadSlowdownCooldownTime;
private:
byte _currentPalette[768];

View File

@ -40,14 +40,14 @@ namespace Director {
uint32 DirectorEngine::getMacTicks() { return (g_system->getMillis() * 60 / 1000.) - _tickBaseline; }
bool DirectorEngine::processEvents(bool captureClick) {
bool DirectorEngine::processEvents(bool captureClick, bool skipWindowManager) {
debugC(5, kDebugEvents, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
debugC(5, kDebugEvents, "@@@@ Processing events");
debugC(5, kDebugEvents, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
if (!_wm->processEvent(event)) {
if (skipWindowManager || !_wm->processEvent(event)) {
// We only want to handle these events if the event
// wasn't handled by the window manager.
switch (event.type) {
@ -71,7 +71,7 @@ bool DirectorEngine::processEvents(bool captureClick) {
if (captureClick)
return true;
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
if (captureClick)
return true;
break;
@ -261,6 +261,7 @@ bool Movie::processEvent(Common::Event &event) {
_currentHiliteChannelId = 0;
_mouseDownWasInButton = false;
g_director->loadSlowdownCooloff();
return true;
case Common::EVENT_KEYDOWN:
@ -276,6 +277,7 @@ bool Movie::processEvent(Common::Event &event) {
_lastTimeOut = _lastEventTime;
queueUserEvent(kEventKeyDown);
g_director->loadSlowdownCooloff();
return true;
case Common::EVENT_KEYUP:

View File

@ -100,6 +100,11 @@ struct SaveFilePath {
};
static void quirkWarlock() {
g_director->_loadSlowdownFactor = 150000; // emulate a 1x CD drive
g_director->_fpsLimit = 15;
}
static void quirkLimit15FPS() {
g_director->_fpsLimit = 15;
}
@ -175,6 +180,12 @@ struct Quirk {
Common::Platform platform;
void (*quirk)();
} quirks[] = {
// Spaceship Warlock is designed to run as quickly as possible on a
// single speed CD drive; there's often content just before a movie
// transition which would otherwise get skipped past.
{ "warlock", Common::kPlatformMacintosh, &quirkWarlock },
{ "warlock", Common::kPlatformWindows, &quirkWarlock },
// Eastern Mind sets the score to play back at a high frame rate,
// however the developers were using slow hardware, so some
// animations play back much faster than intended.
@ -253,6 +264,11 @@ void DirectorEngine::gameQuirks(const char *target, Common::Platform platform) {
}
}
void DirectorEngine::loadSlowdownCooloff(uint32 delay) {
if (_loadSlowdownFactor)
_loadSlowdownCooldownTime = g_system->getMillis() + delay;
}
/*****************
* CachedArchive
*****************/

View File

@ -500,6 +500,7 @@ Common::String rectifyRelativePath(const Common::String &path, const Common::Pat
}
Common::String result = "@:" + Common::Path::joinComponents(components).toString(g_director->_dirSeparator);
debug(9, "rectifyRelativePath(): '%s' + '%s' => '%s'", base.toString(g_director->_dirSeparator).c_str(), path.c_str(), result.c_str());
warning("rectifyRelativePath(): '%s' + '%s' => '%s'", base.toString(g_director->_dirSeparator).c_str(), path.c_str(), result.c_str());
return result;
}

View File

@ -443,6 +443,32 @@ bool Window::loadNextMovie() {
return false;
probeResources(mov);
// Artificial delay for games that expect slow media, e.g. Spaceship Warlock
if (g_director->_loadSlowdownFactor && !debugChannelSet(-1, kDebugFast)) {
// Check that we're not cooling down from skipping a delay.
if (g_system->getMillis() > g_director->_loadSlowdownCooldownTime) {
uint32 delay = mov->getFileSize() * 1000 / g_director->_loadSlowdownFactor;
debugC(5, kDebugLoading, "Slowing load of next movie by %d ms", delay);
while (delay != 0) {
uint32 dec = MIN((uint32)10, delay);
// Skip delay if mouse is clicked
if (g_director->processEvents(true, true)) {
g_director->loadSlowdownCooloff();
break;
}
g_director->_wm->replaceCursor(Graphics::kMacCursorWatch);
g_director->draw();
g_system->delayMillis(dec);
delay -= dec;
}
}
// If this movie switch is within the cooldown time,
// don't add a delay. This is to allow for rapid navigation.
// User input events will call loadSlowdownCooloff() and
// extend the cooldown time.
}
_currentMovie = new Movie(this);
_currentMovie->setArchive(mov);