现在,让我们来把前面章节中缺失的部分补上。在我们这部分内容的最后,我们会得到一个完整的代码。
怪物插件
基于前面我们介绍的插件框架,我故意创建了四种不同类型的插件:
- 纯 C++ 插件
- 纯 C 插件
- 部署为动态/共享库的混合插件
- 应当直接链接到可执行文件中的 C++ 静态插件
所有这些插件都会将其怪物对象作为 actor 注册到PluginManager
。另外,游戏本身提供了Hero
类,作为实现了IActor
接口的对象。由游戏控制的Hero
类和其他怪物对象的绝大多数IActor
接口的代码都是一致的。游戏也会提供一些内建的怪物。
动态 C++ 插件
动态 C++ 插件注册了KillerBunny
和 StationarySatan
两种怪物。下面的代码是 KillerBunny.h 头文件。KillerBunny
直接实现了 C++IActor
接口(这使得它成为一个纯 C++ 插件)。它实现了create()
和 destroy()
static 函数,用于PluginManager
创建和销毁对象。StationarySatan
以及其他纯 C++ 插件对象基本类似(除了那些 private 数据)。
#ifndef KILLER_BUNNY_H #define KILLER_BUNNY_H #include <object_model/object_model.h> struct PF_ObjectParams; class KillerBunny : public IActor { public: // static plugin interface static void * create(PF_ObjectParams *); static apr_int32_t destroy(void *); ~KillerBunny(); // IActor methods virtual void getInitialInfo(ActorInfo * info); virtual void play(ITurn * turnInfo); private: KillerBunny(); }; #endif
下面的代码包含了create()
和 destroy()
的实现。这些都很简单。create()
函数创建了一个新的KillerBunny
对象并将其返回(以void *
的形式)。destroy()
函数需要一个void *
作为参数,该指针必须是 create()
函数返回的。它会将void *
转换成KillerBunny *
然后 delete 掉。这里最重要的部分是,这些函数允许PluginManager
不需要“知道”任何关于 KillerBunny
类的信息,就可以创建KillerBunny
对象。返回的实例则可以通过IActor
接口使用(即使它返回的是一个void *
)。
void * KillerBunny::create(PF_ObjectParams *) { return new KillerBunny(); } apr_int32_t KillerBunny::destroy(void * p) { if (!p) return -1; delete (KillerBunny *)p; return 0; }
下面一段代码包含IActor
接口的实现。这些函数也很简单。getInitialInfo()
函数用于使用一些数据组装ActorInfo
结构。play()
函数才是KillerBunny
实际提供的功能:移动、攻击或回避敌人等。这里,它仅仅从ITurn
接口获取到友军的列表。这只是因为我们不需要提供一个完整的实现,事实上,我们的这些怪物什么都做不了。只有 Hero
才能真正的攻击。在怪物受到攻击时,它们需要保护自己并反击。
void KillerBunny::getInitialInfo(ActorInfo * info) { ::strcpy((char *)info->name, "KillerBunny"); info->attack = 10; info->damage = 3; info->defense = 8; info->health = 20; info->movement = 2; // Irrelevant. Will be assigned by system later info->id = 0; info->location_x = 0; info->location_y = 0; } void KillerBunny::play(ITurn * turnInfo) { IActorInfoIterator * friends = turnInfo->getFriends(); }
我们写这些代码的目的是演示,编写纯 C++ 插件是多么容易。除了公式化的create()
和 destroy()
static 函数,你只需要实现一个标准 C++ 类,没有任何神秘的代码需要添加。
下面是插件初始化代码。这些代码并不复杂,但是很繁琐并且容易出错:在PF_initPlugin
函数中定义一个退出函数以及一个PF_RegisterParams
结构体,然后将它们组装到一起,并且要注册所有插件对象。确保在初始化失败时返回 NULL。除此之外就没有别的什么了。这就是编写纯 C++ 怪物插件所需要的(在下面的代码中,我们注册了两个怪物)。
#include "cpp_plugin.h" #include "plugin_framework/plugin.h" #include "KillerBunny.h" #include "StationarySatan.h" extern "C" PLUGIN_API apr_int32_t ExitFunc() { return 0; } extern "C" PLUGIN_API PF_ExitFunc PF_initPlugin(const PF_PlatformServices * params) { int res = 0; PF_RegisterParams rp; rp.version.major = 1; rp.version.minor = 0; rp.programmingLanguage = PF_ProgrammingLanguage_CPP; // Register KillerBunny rp.createFunc = KillerBunny::create; rp.destroyFunc = KillerBunny::destroy; res = params->registerObject((const apr_byte_t *)"KillerBunny", &rp); if (res < 0) return NULL; // Regiater StationarySatan rp.createFunc = StationarySatan::create; rp.destroyFunc = StationarySatan::destroy; res = params->registerObject((const apr_byte_t *)"StationarySatan", &rp); if (res < 0) return NULL; return ExitFunc; }