DynRPG  v0.32 Unofficial
Plugin SDK
Getting Started
Note
This page describes how to get started with the SDK. If you want to know how to get started with the Patch, see The Patch.

The right C++ Compiler

The DynRPG SDK works on a very low level. This means, small differences between compilers can break everything. The DynRPG was developed and tested using GCC, thus you should use GCC too. This DynRPG version only works with GCC version 4.7.1 and higher. You might use the Code::Blocks IDE, for example.

Installation

Installing the SDK is easy - all you need to do is copying the files from the include and lib directories of the download archive into the corresponding folders of your compiler environment.

Header and library

In order to use the DynRPG in your C++ project, you need to include DynRPG/DynRPG.h:

#include <DynRPG/DynRPG.h>
Warning
If you use more than one code module, you need to put the following line before the include statement in all modules except one:
#define NOT_MAIN_MODULE

DynRPG automatically creates a DllMain function for you which stores the plugin's instance handle in a global variable called hInstance. You can suppress this behaviour by defining CUSTOM_DLLMAIN before including the header file.

The header file automatically includes the headers string, map, windows.h because they are needed for the declarations.

Warning
Since the RPG Maker is Non-Unicode, your plugin should be Non-Unicode too!

You also need to add the library DynRPG (libDynRPG.a) to your project. See your IDE's help file if you don't know how to do this. (Normally you need to add the parameter -lDynRPG to the linker's command line.)

Callbacks

Together with the Game objects, callbacks are important ways to interface your plugin with the game. Callbacks are functions which you can define in your plugin. On certain events in the game (for example, a picture is drawn - see onDrawPicture) certain callbacks will be called by DynRPG. Callbacks either return void or bool. If you return false in bool callbacks, this will prevent other plugins from receiveing the same notification and sometimes it will also prevent some event from happening in the game. For example, if you return false from your onDrawPicture callback, the picture won't be drawn and other plugins (after yours) won't receive onDrawPicture for that picture.

You only need to define those callback functions which you actually use.

For a list of available callbacks, click here: Callbacks

Game objects

The game objects are the second important way to interface your plugin with the game. Most of the RPG classes are tied to a game object and should only be used through it. For example, the game object tied to RPG::Actor is called RPG::actors.

Game objects represent certain "things" in the game. For example, the RPG::map object (the corresponding class is called RPG::Map) represents the current map. So, to get the width of the current map, you may call the RPG::Map::getWidth function like this:

int mapWidth = RPG::map->getWidth();

For a list of available game objects, click here: Game objects

Hello World - Our first plugin

It's time to create our first plugin. It's going to be really simple - all it is going to do is displaying a message box asking whether we really want to play before actually starting the game. We'll call it are_you_sure.

First, create an RPG Maker 2003 project to test the plugin and apply The Patch to it. Then, create a new C++ project in your favorite IDE and select DLL as project type. It might be convinient to store the source code in a subfolder of the DynPlugins directory of your test project and set the compiler output directory to the DynPlugins directory.

Warning
If you are using Windows Vista or Windows 7, make sure you have write access to your project directory! You better put it into your Documents folder. Otherwise make sure you run both RPG Maker 2003 and your IDE (e.g. Code::Blocks) with administrator privileges!

Okay, I'll be give a bit more step-by-step instructions now - of course you can do it differently if you know how, but this should help people who are not familiar with Code::Blocks to get started:

  1. Download Code::Blocks here. Make sure you select the "bigger" package which contains MinGW which includes the GNU C++ compiler (which we need).
  2. Install Code::Blocks. It's okay if you don't change the default feature selection. Please remember or write down to path you are installing Code::Blocks to (e.g. C: Files (x86))!
  3. Open the path where you installed Code::Blocks in Windows Explorer.
  4. There should be a subfolder called MinGW. Open it. There should be folders called include and lib inside, among others.
  5. Copy the include and lib folders from your DynRPG download (in the sdk subfolder) into the MinGW folder. Confirm merging the folders.
  6. Start Code::Blocks.
  7. Click Create new project....
  8. Select Dynamic Link Library and click Go.
  9. Click Next.
  10. Enter the name of your plugin (here: are_you_sure). To avoid problems later, do not use spaces.
  11. In the second field, select the DynPlugins folder of your RM2k3 test project. The actual filename of the Code::Blocks project file (shown in the last field) should be similar to this: C:\Users\My Username\Documents\My Test Project.cbp
  12. Click Next.
  13. Make sure GNU GCC Compiler is selected in the first field.
  14. Uncheck Create "Debug" configuration.
    Note
    Of course, if you know how you use them, you may create debug builds too. As said, these step-by-step instructions are for beginners.
  15. Change the Output dir. at the "Release" options to ..\ (two dots and then a backslash). This will create the plugin DLL directly in the DynRPG folder if you have followed step 11 correctly.
  16. Click Finish.
  17. Open the Project menu, select Build options....
  18. Go to the Linker settings tab.
  19. At the Link libraries section, click Add.
  20. Enter DynRPG and click OK.
  21. Go to the pre/post build steps tab.
  22. Enter the following line into the bottom box: cmd /c del ..*.a & del ..*.def
  23. Click OK again.
  24. Open the Project menu, select Set programs' arguments....
  25. Click the button at the right end of the Host application field and select your game's RPG_RT.exe file. This will allow you to use the Run and Build and run buttons in Code::Blocks to run your game.
  26. If you want the game to automatically start in windowed test play mode when you start it from Code::Blocks, put TestPlay ShowTitle Window into the Program arguments field.
  27. Click OK.
  28. Now, Code::Blocks has created two files for you: main.cpp (find it in the Sources folder of the tree view) and main.h (find it in the Headers folder of the tree view). Delete the contents of both of them. In our examples, we don't need the main.h file at all, you may right-click it and choose Remove file from project if you want. In main.cpp, copy the code provided below:
#include <DynRPG/DynRPG.h>
// Handler called on startup
bool onStartup(char *pluginName) {
if(MessageBox(
NULL, // We don&#39;t need a window handle
"This is such a haaaaard game. Are you SURE you want to play it now?", // Text
"The Are You Sure Plugin", // Title
MB_YESNO | MB_ICONQUESTION // Flags (yes/no buttons and question icon)
) == IDYES) {
// The user clicked "Yes", we may continue
return true;
} else {
// The user clicked "No", so we need to abort
return false;
}
}

Okay, that's really simple. It uses Windows' MessageBox function to display a message. If the user clicked "Yes", the game will continue, otherwise it will not start.

This simple plugin uses the onStartup callback. This callback is called before the game starts. If you return false in this callback, the game won't start. (You only need to define those callbacks which you want to use.)

So, if you compile this plugin, make sure the DLL file (are_you_sure.dll) was put into the DynPlugins directory and your test project was successfully patched using The Patch, you should see the DynRPG logo when you start the game, followed by our message box.

By the way, the DynRPG logo cannot be removed. Yes, that's intentional.

Condition Icons - More Advanced

Enough of nonsense. Now we are going to create something useful. We are going to create a plugin which displays the conditions of actors and monsters over their head, as icons.

These are its features:

First, in the onStartup handler, we will load the configuration and store it in a global variable called configuration.

Then, in the onBattlerDrawn handler, we will iterate through all conditions the battler has, check if the images we need are loaded, if not, load them. We will also calculate the total width of all icons for that battler here. Then we will draw all the icons and finally clear the RPG::Battler::displayedConditions array so that no info window is displayed when the target selection is active.

Finally, in the onExit handler, we will unload all images.

This is the content of our condition_icons.cpp file:

#include <DynRPG/DynRPG.h>
#include <sstream> // For std::stringstream
// We store the configuration here
std::map<std::string, std::string> configuration;
// We store the images of the conditions here
std::map<int, RPG::Image *> images;
// This handler is called on startup
bool onStartup(char *pluginName) {
// We load the configuration from the DynRPG.ini file here
configuration = RPG::loadConfiguration(pluginName);
return true; // Don&#39;t forget to return true so that the start of the game will continue!
}
// This handler is called after a battler is drawn
bool onBattlerDrawn(RPG::Battler *battler, bool isMonster, int id) {
int totalWidth = 0; // We store the total width of the condition icons here
// We loop through all the elements of the battler&#39;s "conditions" array here
// Note that the "condition" array is one-based, thus we start at index 1
for(int i = 1; i <= battler->conditions.size; i++) {
// If the battler has the condition (see documentation)...
if(battler->conditions[i] > 0) {
// If the image isn&#39;t loaded yet...
if(!images[i]) {
// First, we create an RPG::Image object
images[i] = RPG::Image::create();
// Yes, we want to use the transparent color
images[i]->useMaskColor = true;
// Now, we put the key name for the configuration entry together
// It should be "Condition12" for condition 12, for example
std::stringstream keyName;
keyName << "Condition" << i;
// Now, we try to load the image. If loading the image fails,
// nothing special will happen (because of the "false" at the
// "showErrors" parameter), the image will just be empty.
images[i]->loadFromFile(configuration[keyName.str()], false);
}
// We add the image&#39;s width to the total width
totalWidth += images[i]->width;
}
}
// Now we need to know the Y coordinate of the top of the battler
int topY;
if(isMonster) {
// It is a monster. We just use the monster&#39;s image to find out
// its size.
RPG::Monster *monster = (RPG::Monster *)battler; // First, we cast the "battler" to a "Monster"
topY = monster->y - monster->image->height / 2; // Now we calculate the top position
} else {
// Okay, since we don&#39;t have a way to find out the size of an actor&#39;s
// battle graphic, we will just "guess" that it&#39;s a normal BattleCharSet
// which is 48 pixels tall.
// In a "good" plugin, there would be a way to set the actual height
// for each actor in the configuration, but this would be too much
// for this tutorial.
topY = battler->y - 48 / 2;
}
// We will use this variable to store the current X coordinate while we
// draw the images. We will increase this variable every time we draw an
// image.
int currentX = battler->x - totalWidth / 2;
// Okay, let&#39;s loop again through the conditions. This is necessary
// because we first had to find out the total width before we could
// start drawing the images. Now we will draw them.
for(int i = 1; i <= battler->conditions.size; i++) {
// If the battler has the condition (see documentation)...
if(battler->conditions[i] > 0) {
// Okay, here we actually draw the image on the screen!
RPG::screen->canvas->draw(currentX, topY - images[i]->height, images[i]);
// And we increase the current X coordinate.
currentX += images[i]->width;
}
}
// Clear the condition cache which is normally used to determine which
// conditions should be shown in the "info window" on top of the screen
// during target selection (we don&#39;t need to display the conditions twice!)
for(int i = 0; i < 5; i++) {
battler->displayedConditions[i] = 0;
}
return true; // It&#39;s okay that the battler is drawn, so we return true. Don&#39;t forget that!
}
// This handler is called when the game exits
void onExit() {
// We will unload all images here.
// If you are not familiar with C++ iterators: This "for" loop just iterates
// through all items in "images".
for(std::map<int, RPG::Image *>::const_iterator iter = images.begin(); iter != images.end(); ++iter ) {
// The reason we don&#39;t use images[iter->first] instead of iter->second
// is that RPG::Image::destroy also takes a reference and sets the
// parameter to zero at the end.
RPG::Image::destroy(images[iter->first]);
}
}

To try it, we just need to put the compiled DLL into the DynPlugins folder (if it isn't already there). Then we need to get some icons. For testing, I made some very simple 16x16 icons and put them into the Picture folder.

Then we need to open our DynRPG.ini and put the filenames in it, like this:

[condition_icons]
Condition2=Picture\poison.png
Condition3=Picture\blind.png
Condition5=Picture\berserk.png
Condition6=Picture\confused.png
Condition7=Picture\sleep.png

And hey presto, this is what we get:

You can download the source code as well as the finished plugin here.

What next?

You may now start developing your own plugins. You may first take a look at the Callbacks, the Game objects, and the members of the RPG namespace, to get an overview of what is possible and how to get to the result you want. Then, you might start with something easy (like a DynRPG-based version of the PicPointerPatch - hint: take a look at onEventCommand, RPG::EventScriptLine and RPG::variables), then expand your knowledge until you can do nearly everything you want... or something like that. :-)

Yes, I know, I am just throwing you out into the wilderness. Sorry for not providing you more examples how to use all the features. I just had no time for it yet. If you are clever, you will probably be able to create something useful with what you got in this documentation anyway. If not, more examples will come. I will create nice plugins and publish them with source code, etc.

Oh, and don't forget to read and follow the Rules and guidelines for plugin developers!

By the way: If you want to publish your plugin, it would be nice if you would publish the source code too.

Have fun!
Best regards, Cherry