Difference between revisions of "HOWTO-Engines"

Jump to navigation Jump to search
770 bytes added ,  01:16, 9 June 2008
Extracted detection.cpp from quux.cpp, added information about the MetaEngine and updated the example to work in trunk
(Extracted detection.cpp from quux.cpp, added information about the MetaEngine and updated the example to work in trunk)
Line 16: Line 16:
# Add a new directory <tt>engines/quux/</tt>
# Add a new directory <tt>engines/quux/</tt>
# Add <tt>engines/quux/module.mk</tt> (take a look at module.mk files of 2-3 existing engines to understand the content).
# Add <tt>engines/quux/module.mk</tt> (take a look at module.mk files of 2-3 existing engines to understand the content).
# Add <tt>engines/quux/quux.h</tt> and <tt>engines/quux/quux.cpp</tt>; this will contain your Engine subclass (or at least parts of it). It will also contain the plugin interface code (more on that in the next section).
# Add <tt>engines/quux/quux.h</tt> and <tt>engines/quux/quux.cpp</tt>; this will contain your Engine subclass (or at least parts of it).
# Add <tt>engines/quux/detection.cpp</tt>; It will contain the plugin interface code (more on that in the next section).
# Modify <tt>engines/engines.mk</tt> by adding your engine. It should be clear what to do by looking at what is done for the other engines there.
# Modify <tt>engines/engines.mk</tt> by adding your engine. It should be clear what to do by looking at what is done for the other engines there.
# Modify <tt>configure</tt> by adding a new add_engine line. Again, just check out what is done for the existing engines.
# Modify <tt>configure</tt> by adding a new add_engine line. Again, just check out what is done for the existing engines.
Line 48: Line 49:
|inter.cpp, logic.cpp, script.cpp || Game logic, resp. script/bytecode interpreter
|inter.cpp, logic.cpp, script.cpp || Game logic, resp. script/bytecode interpreter
|}
|}
=== Subclassing MetaEngine ===
Let's implement the plugin interface: You'll have to create a custom MetaEngine subclass which provides the information and functionality related to the engine that can be used without running any game. That includes detecting games, listing savegames and instancing the engine. You have to specify your MetaEngine class to the REGISTER_PLUGIN_* macros. (See the example below)
You can alternatively subclass AdvancedMetaEngine from <tt>common/advancedDetector.h</tt> to reuse some common functionality like MD5 based game detection.


=== Subclassing Engine ===
=== Subclassing Engine ===
TODO: We should probably give some sample code, maybe even provide a full (empty) Engine demo class. Maybe even provide a real mini engine project somewhere on our site which demonstrates using events, drawing, etc. ? Not sure whether this would be worth the effort, though.
TODO: We should probably give some sample code, maybe even provide a full (empty) Engine demo class. Maybe even provide a real mini engine project somewhere on our site which demonstrates using events, drawing, etc. ? Not sure whether this would be worth the effort, though.
TODO: At the very least, describe the plugin interface: I.e. which functions *must* be implemented, and what they are supposed to do. Once again, sample code would be nice.


Important: If you're using the ScummVM GUI (g_gui and stuff) you have always to call g_gui.handleScreenChanged() if you received a OSystem::EVENT_SCREEN_CHANGED event, else it could be that your gui looks strange or even crashes ScummVM.
Important: If you're using the ScummVM GUI (g_gui and stuff) you have always to call g_gui.handleScreenChanged() if you received a OSystem::EVENT_SCREEN_CHANGED event, else it could be that your gui looks strange or even crashes ScummVM.
Line 92: Line 96:


Additionally ScummVM offers way of recording all events and then playing them back on request. That could be used for "demoplay" mode. But to ensure that it will work for your engine, you have to register your RandomSource class instance. See example engine below.
Additionally ScummVM offers way of recording all events and then playing them back on request. That could be used for "demoplay" mode. But to ensure that it will work for your engine, you have to register your RandomSource class instance. See example engine below.
== Example ==


=== Example: engines/quux/quux.h ===
=== Example: engines/quux/quux.h ===
Line 149: Line 155:
#include "common/fs.h"
#include "common/fs.h"


#include "base/game.h"
#include "quux/quux.h"
#include "base/plugins.h"
 
namespace Quux {
 
QuuxEngine::QuuxEngine(OSystem *syst)
: Engine(syst) {
// Put your engine in a sane state, but do nothing big yet;
// in particular, do not load data from files; rather, if you
// need to do such things, do them from init().
 
// Do not initialize graphics here
 
// However this is the place to specify all default directories
Common::File::addDefaultDirectory(_gameDataPath + "sound/");
 
// Here is the right place to set up the engine specific debug levels
Common::addSpecialDebugLevel(kQuuxDebugExample, "example", "this is just an example for a engine specific debug level");
Common::addSpecialDebugLevel(kQuuxDebugExample2, "example2", "also an example");
 
// Don't forget to register your random source
syst->getEventManager()->registerRandomSource(_rnd, "quux");
 
printf("QuuxEngine::QuuxEngine\n");
}
 
QuuxEngine::~QuuxEngine() {
// Dispose your resources here
printf("QuuxEngine::~QuuxEngine\n");
 
// Remove all of our debug levels here
Common::clearAllSpecialDebugLevels();
}
 
int QuuxEngine::init() {
// Initialize graphics using following template
// You have to use transactions
_system->beginGFXTransaction();
// This is handled by base Engine class and processes command
// line parameters
initCommonGFX(true);
 
// Specify dimensions of game graphics window.
// In this example: 320x200
_system->initSize(320, 200);
_system->endGFXTransaction();
// Create debugger console. It requires GFX to be initialized
_console = new Console(this);
 
// Additional setup.
printf("QuuxEngine::init\n");
return 0;
}
 
int QuuxEngine::go() {
// Your main even loop should be (invoked from) here.
printf("QuuxEngine::go: Hello, World!\n");
 
// This test will show up if -d1 and --debugflags=example are specified on the commandline
debugC(1, kQuuxDebugExample, "Example debug call");
 
// This test will show up if --debugflags=example or --debugflags=example2 or both of them and -d3 are specified on the commandline
debugC(3, kQuuxDebugExample | kQuuxDebugExample2, "Example debug call two");
 
return 0;
}


#include "engines/metaengine.h"
} // End of namespace Quux
</pre>


=== Example: engines/quux/detection.cpp ===
The following example implements a custom MetaEngine instead of using the AdvancedMetaEngine.
<pre>
#include "quux/quux.h"
#include "quux/quux.h"


#include "base/game.h"
#include "base/plugins.h"
#include "engines/metaengine.h"


static const PlainGameDescriptor quux_setting[] = {
static const PlainGameDescriptor quux_setting[] = {
Line 168: Line 245:
return "Quux the Example Module";
return "Quux the Example Module";
}
}
virtual const char *getCopyright() const {
virtual const char *getCopyright() const {
return "Copyright (C) Quux Entertainment Ltd.";
return "Copyright (C) Quux Entertainment Ltd.";
Line 214: Line 292:
assert(syst);
assert(syst);
assert(engine);
assert(engine);
 
// Scan the target directory for files (error out if it does not exist)
// Scan the target directory for files (error out if it does not exist)
FSList fslist;
FSList fslist;
Line 221: Line 299:
return kInvalidPathError;
return kInvalidPathError;
}
}
 
// Invoke the detector
// Invoke the detector
Common::String gameid = ConfMan.get("gameid");
Common::String gameid = ConfMan.get("gameid");
GameList detectedGames = detectGames(fslist);
GameList detectedGames = detectGames(fslist);
 
for (uint i = 0; i < detectedGames.size(); i++) {
for (uint i = 0; i < detectedGames.size(); i++) {
if (detectedGames[i].gameid() == gameid) {
if (detectedGames[i].gameid() == gameid) {
Line 234: Line 311:
}
}
}
}
 
// Failed to find any game data
// Failed to find any game data
return kNoGameDataFoundError;
return kNoGameDataFoundError;
Line 240: Line 317:
};
};


 
#if PLUGIN_ENABLED_DYNAMIC(QUUX)
REGISTER_PLUGIN(QUUX, QuuxMetaEngine);
REGISTER_PLUGIN_DYNAMIC(QUUX, PLUGIN_TYPE_ENGINE, QuuxMetaEngine);
 
#else
 
REGISTER_PLUGIN_STATIC(QUUX, PLUGIN_TYPE_ENGINE, QuuxMetaEngine);
namespace Quux {
#endif
 
 
QuuxEngine::QuuxEngine(OSystem *syst)
: Engine(syst) {
// Put your engine in a sane state, but do nothing big yet;
// in particular, do not load data from files; rather, if you
// need to do such things, do them from init().
 
// Do not initialize graphics here
 
// However this is the place to specify all default directories
Common::File::addDefaultDirectory(_gameDataPath + "sound/");
 
// Here is the right place to set up the engine specific debug levels
Common::addSpecialDebugLevel(kQuuxDebugExample, "example", "this is just an example for a engine specific debug level");
Common::addSpecialDebugLevel(kQuuxDebugExample2, "example2", "also an example");
 
// Don't forget to register your random source
syst->getEventManager()->registerRandomSource(_rnd, "quux");
 
printf("QuuxEngine::QuuxEngine\n");
}
 
QuuxEngine::~QuuxEngine() {
// Dispose your resources here
printf("QuuxEngine::~QuuxEngine\n");
 
// Remove all of our debug levels here
Common::clearAllSpecialDebugLevels();
}
 
int QuuxEngine::init() {
// Initialize graphics using following template
// You have to use transactions
_system->beginGFXTransaction();
// This is handled by base Engine class and processes command
// line parameters
initCommonGFX(true);
 
// Specify dimensions of game graphics window.
// In this example: 320x200
_system->initSize(320, 200);
_system->endGFXTransaction();
// Create debugger console. It requires GFX to be initialized
_console = new Console(this);
 
// Additional setup.
printf("QuuxEngine::init\n");
return 0;
}
 
int QuuxEngine::go() {
// Your main even loop should be (invoked from) here.
printf("QuuxEngine::go: Hello, World!\n");
 
// This test will show up if -d1 and --debugflags=example are specified on the commandline
debugC(1, kQuuxDebugExample, "Example debug call");
 
// This test will show up if --debugflags=example or --debugflags=example2 or both of them and -d3 are specified on the commandline
debugC(3, kQuuxDebugExample | kQuuxDebugExample2, "Example debug call two");
 
return 0;
}
 
 
} // End of namespace Quux
</pre>
</pre>


Line 325: Line 335:


# This module can be built as a plugin
# This module can be built as a plugin
ifdef BUILD_PLUGINS
ifeq ($(ENABLE_QUUX), DYNAMIC_PLUGIN)
PLUGIN := 1
PLUGIN := 1
endif
endif
960

edits

Navigation menu