Difference between revisions of "Advanced Engine Features"
(Added page describing some of the 'advanced' features ScummVM Engine's may implement (WIP)) |
(No difference)
|
Revision as of 18:51, 4 November 2008
In the following, some of the more advanced features a ScummVM Engine may implement are described. We describe what advantage each feature gives to the engine author respectively to engine users, and sketch how to implement support for it. Features are roughly grouped and sorted by mutual dependency.
Enhanced user interaction
Improved pause support
What is this about?
Sometimes ScummVM needs to pause a currently running engine, for example, to show the GMM ("Global Main Menu"), or maybe on a cellphone to switch it to the background during an incoming call.
A simple way for that is not return from OSystem::pollEvent() until the condition ends. But that is problematic because your internal timers, as well as audio, probably keep running (a port can work around this to an extent, but not always perfectly).
Therefore, we added an Engine API that allows pausing and resuming the currently running engine.
How to implement it
You have to overload the Engine::pauseEngineIntern() method. The default method does exactly one thing: It invokes Mixer::pauseAll() to pause/resume the digital audio mixer.
But if your engine is e.g. using MIDI, then you might want to pause/resume that as well. You also might wish to record the time the pause started, to be able to adjust any internal timers once the pause ends.
That's all you need to implement, but if your engine needs to keep track of a pause state anyway, you can also use the public API built atop this internal function. It supports recursive pausing (if the engine is paused 5 times, it has to be unpaused 5 times before actually resuming) for free.
Relevant Engine API <syntax type="C++"> virtual void pauseEngineIntern(bool pause); void pauseEngine(bool pause); bool isPaused() const; </syntax>
Already implemented by: AGOS, Gob, Kyra, Lure, SCUMM, Sword2
RTL ("Return to Launcher") support
What is this about?
This feature allows the user to return from the current game back to the Launcher, instead of having to first quit ScummVM and then restart it (which on some ScummVM ports amounts to having to reboot).
How to implement it
You can implement this by checking for and honoring the EVENT_RTL event. A much easier way, which also gives you some other advantages (e.g. this also covers EVENT_QUIT), is to regularly poll the return value of Engine::shouldQuit(). If it returns true, you should break out from your main game loop and your Engine::go() method should return to the caller.
Relevant Engine API <syntax type="C++"> void quitGame(); bool shouldQuit() const;
kSupportsRTL feature flag</syntax>
Already implemented by: AGI, AGOS, Cine, Gob, Kyra, Lure, Parallaction, Queen, SAGA, SCUMM, Sky, Sword1, Sword2, Touche
Global options dialog support
What is this about?
The global options dialog is reachable from the GMM and allows the user to modify various settings; settings which might not otherwise be editable while a game is running (depending on the engine). Right now, it only shows sound and subtitle settings, but more settings will be added in the future.
How to implement it
This comes for free with the GMM (see below). But depending on how your engine works, you may have to resync your internal state with the sound and subtitle settings in the config manager. For this, implement the syncSoundSettings() method.
Relevant Engine API <syntax type="C++"> virtual void syncSoundSettings();</syntax>
Already implemented by: AGI, AGOS, Lure, Queen, SAGA, SCUMM, Sword1, Touche
GMM ("Global Main Menu") support
What is this about?
This is a special dialog built using the ScummVM native GUI, which can be fired up in any Engine ScummVM supports, at virtually any time (to be precise, whenever the engine is polling for events). Right now, the trigger is F6 globally, but that will change (in particular, F6 may and will clash with some games).
The idea is to give the user a uniform way to access certain functionality everywhere: In particular, access to a small global options dialog; the ability to quit and/or return to the launcher; the about dialog and version information; and to load/save the gamestate.
How to implement it
You do not really have to do anything for the GMM to show up in your engine. But for an optimal user experience, you should implement several other features, including pause support, RTL support, Global options dialog support, and support for runtime loading/saving.
Relevant Engine API
None.
Already implemented by: not applicable
Enhanced load/save support
In this section, we present various MetaEngine and Engine APIs which greatly experience the user experience with regards to savestates.
Listing savestates via command line or Launcher
What is this about?
TODO
How to implement it
TODO
Relevant MetaEngine API <syntax type="C++"> virtual SaveStateList listSaves(const char *target) const;
kSupportsListSaves feature flag</syntax>
Already implemented by: AGI, AGOS, Cine, Kyra, Lure, Parallaction, Queen, SAGA, SCUMM, Sky, Sword1, Sword2, Tinsel, Touche
Loading savestates via command line or Launcher
What is this about?
TODO
How to implement it
TODO
Relevant MetaEngine API <syntax type="C++"> save_slot ConfigMan setting
kSupportsLoadingDuringStartup feature flag</syntax>
Already implemented by: AGI, Cine, Kyra, Lure, Parallaction, Queen, SAGA, SCUMM, Sky, Sword1, Sword2, Touche
Deleting savestates via the Launcher and GMM
What is this about?
This feature allows the user to delete existing savestates from the Launcher's load dialog, and also from the GMM's load dialog.
How to implement it
First off, your engine must overload Engine::hasFeature() to return true when kSupportsDeleteSave is passed in.
Furthermore, it has to implement MetaEngine::removeSaveState(): This should delete the savestate for the specified via SaveFileManager::removeSavefile(). In addition, if your engine keeps an index of all existing saves (something which we strongly discourage, as it makes it more difficult for the user to transfer savestates from one device to another), then you have to remove references to savestate from that index.
Because of the way it is exposed, the user can only use this feature if you also implement listing savestates.
Relevant MetaEngine API <syntax type="C++"> void removeSaveState(const char *target, int slot) const;
kSupportsDeleteSave feature flag</syntax>
Already implemented by: AGI, Kyra, Lure, Parallaction, Queen, SAGA, SCUMM, Sword2, Touche
Savestate metadata support
What is this about?
The load dialog provided by the Launcher and the GMM supports displaying a thumbnail of an in-game screenshot of a savestate to the user, as well as some other metadata (currently, besides the thumbnail it can display the date the savestate was created, and how long the user already was playing). For this, it needs to query the MetaEngine for this metadata.
How to implement it
Implemented MetaEngine::querySaveMetaInfos() to return a SaveStateDescriptor similar to the one returned by listSaves(). The main differences are that only a single descriptor is returned, and that this descriptor can be extended by additional attributes. Class SaveStateDescriptor provides several methods for this purpose.
The user can only use this feature if you also implement listing savestates.
Relevant MetaEngine API <syntax type="C++"> virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
kSavesSupportMetaInfo feature flag kSavesSupportThumbnail feature flag kSavesSupportCreationDate feature flag kSavesSupportPlayTime feature flag</syntax>
Already implemented by: Kyra, SCUMM
Loading/Saving during run time
What is this about?
TODO
How to implement it
TODO
Relevant Engine API <syntax type="C++"> virtual int loadGameState(int slot); virtual bool canLoadGameStateCurrently(); virtual int saveGameState(int slot); virtual bool canSaveGameStateCurrently();
kSupportsLoadingDuringRuntime feature flag kSupportsSavingDuringRuntime feature flag</syntax>
Already implemented by: Kyra, SAGA
Misc
Enhanced debug/error messages
What is this about?
Providing more detailed error and debug messages to the engine developer, and to people bug testing an engine, respectively wanting to report a bug.
How to implement it
By implementing the Engine::errorString() method, your engine can add extra info whenever ScummVM is about to print an error message triggered by the error() function. This could be used to print out additional data describing the context, such as the ID of the active script when the error occurred, values of special flags, etc. -- anything that might help debug an error.
If your engine implements a debug console (which is very easy to do using just subclass GUI::Debugger, and then implement your custom debugger commands and variables), overload Engine::getDebugger() to return a point to it. If you do so, when error() is called, it will open that debug console instead of immediately exiting. This can be useful to perform some additional post-mortem analysis.
Relevant Engine API <syntax type="C++"> virtual void errorString(const char *buf_input, char *buf_output); virtual GUI::Debugger *getDebugger();</syntax>
Already implemented by: Kyra, Queen, SCUMM (only engine implementing errorString()), Sky, Sword2