AMX Mod X Documentation
Scripting Tutorial – Basic Plugins
Translated by Shaman.Kaler (again- -b)
前言
你想做个AMXX插件?那首先你应该对Pawn的工作方式有很好的了解,所以确信你已经认真的阅读了《介绍》和《Pawn语言基础》。现在你应该坐在电脑前,看着这部分内容,开着文本编辑器,并且手头就有Small编译器。(老外的废话是那么的多--小克注)你不必急着去编WC3、Matrix Mod或者CSDM那样的插件,不过这至少说明你编插件的心有多么急切……有个很好的AMX Pawn文本编辑器叫Crimson Editor,你可以试一试:
http://www.crimsoneditor.com/(Still available!不过个人还是偏好AMXX Studio滴--小克注)
你应该很熟悉插件的编译方法,也应该懂得如何安装编译完成的插件。在这里方法不再赘述,请阅读前面的部分(我这里没有的说,详见官方文档Plugins部分—小克注)
AMXX插件的结构
AMXX插件主要有四种类型的函数。第一种是Public函数,这是AMXX引擎直接可见的函数。第二种是Native函数,是模块或AMXX内核声明的函数。第三种是用户定义的函数,没有特殊额外声明。第四种是Forward函数,只有当某个特殊事件发生时才被调用(同时也是Public函数)。一个AMXX插件开头必须有一个函数来注册这个插件:
//这使得你可以使用AMXX核心的函数。
//包括(include) includes\amxmodx.inc中定义的所有native函数.
#include <amxmodx>
//声明三个字符串(同样也可以使用预处理命令#define)
new PLUGIN[]="AMXX Demo"
new AUTHOR[]="BAILOPAN"
new VERSION[]="1.00"
//这是个Public函数.
//在AMXX插件中这是必需的注册函数.
//它不需要参数,在地图载入后自动执行.
public plugin_init()
{
//这个函数需要三个字符串.
//这会注册你的插件到AMXX中,同时会设定一些基础信息.
register_plugin(PLUGIN, VERSION, AUTHOR)
}
现在可以尝试编译上面的脚本了,编译出的amxx会非常小——因为没有任何作用。不过,如果你安装并载入了这个插件,然后在控制台输入amxx plugins,你会看到一个叫AMXX Demo的插件的。
建立管理员命令
AMXX提供了一种简单的建立OP控制台命令的方法,通过把命令“register”(注册)成控制台命令来实现。当你注册一个命令的时候需要四个参数:控制台命令、引发的过程函数、OP级别和命令描述。
在这个演示中,我们将建立一个修改玩家HP的插件及其命令”amx_hp”。
我们要做两件事:一是注册控制台命令,二是要在命令上绑定一个Public函数。
#include <amxmodx>
#include <amxmisc> //包含一些有用函数
#include <fun> //包含改HP的函数
new PLUGIN[]="Change Health"
new AUTHOR[]="BAILOPAN"
new VERSION[]="1.00"
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_concmd("amx_hp", "cmd_hp", ADMIN_SLAY, " ")
}
public cmd_hp(id, level, cid)
{
return PLUGIN_HANDLED
}
第一个新函数是”register_concmd”,需要四个参数。第一个是玩家要输入控制台的命令,第二个是将被调用的Public函数,第三个是执行命令的权限,最后一个可以写一些指导性文字(在amx_help命令中用到)。
下面,我们已经建立了一个Public函数用来执行”amx_hp”的功能(cmd_hp),注意我们给了这个函数三个参数,每个参数都代表着一些OP命令需要的特殊数据:id 代表执行命令的玩家id,level 代表该玩家的执行权限,cid 代表命令的内部id。
同时也要注意下PLUGIN_HANDLED。你必须了解有两个主要的返回数据,PLUGIN_CONTINUE代表“一切正常,继续执行”;PLGUIN_HANDLED代表“到此为止,不再继续”。它们的区别虽简单却重要。比如绑定一个命令的时候,你就不该返回PLUGIN_CONTINUE(没得continue,命令执行完就该结束掉—小克注),但是绑定到“say”命令上时,PLUGIN_HANDLED却会完全阻止玩家这句话的显示。不同的情况下你必须小心选择,不过大多数情况下没有什么影响(比如task、event和其他的一些等等)。
现在让我们继续,怎样才能判断一个玩家是不是有ADMIN_SLAY权限呢?
public cmd_hp(id, level, cid)
{
if (!cmd_access(id, level, cid, 3))
return PLUGIN_HANDLED
return PLUGIN_HANDLED
}
上面的cmd_access函数会检查一个命令的信息(命令的使用者,使用者的权限和id)。这个命令会确保两件事:一是使用者有权限,二是OP命令给出的参数达到了最低数量。这里我们把最小值设为3,因为完整的命令会像这样”amx_hp xx 100”,OP命令本身也算做一个参数。如果cmd_access函数不通过,那么我们就让OP命令直接停止。
下一步要解决的问题就是后面的两个参数。我们要读出并解码。HP数量参数很容易,直接从字符串转换到数值即可。另一个就有点难度,因为我们至少要能选出三种不同的人:
• @CT or @T - CTs 或 Ts
• @ALL – 每个人
• Xx -- 玩家的部分名字
public cmd_hp(id, level, cid)
{
if (!cmd_access(id, level, cid, 3))
return PLUGIN_HANDLED
new Arg1[24]
new Arg2[4]
//从控制台获得命令参数(amx_hp后面的部分)
read_argv(1, Arg1, 23)
read_argv(2, Arg2, 3)
//把health转换成数值
new Health = str_to_num(Arg2)
//第一个字母是@吗?
if (Arg1[0] ==
'@')
{
new Team = 0
//确定指定的是哪个队伍.
//注意我们是从空间 [1] 开始, 这是可以的
//这只是意味着我们省掉了”@”
if (equali(Arg1[1], "CT"))
{
Team = 2
} else if (equali(Arg1[1], "T")) {
Team = 1
}
new players[32], num
// 这个函数会把玩家id填充进 players[32] 变量
// num代表玩家的数目
get_players(players, num)
new i
for (i=0; i<num; i++)
{
if (!Team)
{
//设定玩家的HP
set_user_health(players, Health)
} else {
if (get_user_team(players) == Team)
{
set_user_health(players, Health)
}
}
}
} else {
//找到部分名字相同的玩家
//1意味着玩家如果有禁止被选中的权限将不被选中
new player = cmd_target(id, Arg1, 1)
if (!player)
{
//给使用这条命令的玩家发送信息。
//这个命令的格式称为 "format()",
//根据给定的参数显示一定格式的信息。
// %s 代表字符串
// %d 或 %i 代表整数integer
// %f 代表浮点数float
// 因此 "Hello %s, I am %d years old" 后面需要
// 两个参数,一个字符串,一个整数。
console_print(id, "Sorry, player %s could not be found or targetted!", Arg1)
return PLUGIN_HANDLED
} else {
set_user_health(player, Health)
}
}
return PLUGIN_HANDLED
}
最后,这个插件应该是这样的:
#include <amxmodx>
#include <fun>
new PLUGIN[]="Change Health"
new AUTHOR[]="BAILOPAN"
new VERSION[]="1.00"
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_concmd("amx_hp", "cmd_hp", ADMIN_SLAY, "<target> <hp>")
}
public cmd_hp(id, level, cid)
{
if (!cmd_access(id, level, cid, 3))
return PLUGIN_HANDLED
new Arg1[24]
new Arg2[4]
//获得命令参数
read_argv(1, Arg1, 23)
read_argv(2, Arg2, 3)
//把字符串转换成数字
new Health = str_to_num(Arg2)
//第一个字符是@符号吗?
if (Arg1[0] == '@')
{
new Team = 0
if (equali(Arg1[1], "CT"))
{
Team = 2
} else if (equali(Arg1[1], "T")) {
Team = 1
}
new players[32], num
get_players(players, num)
new i
for (i=0; i<num; i++)
{
if (!Team)
{
set_user_health(players
, Health)
} else {
if (get_user_team(players) == Team)
{
set_user_health(players, Health)
}
}
}
} else {
new player = cmd_target(id, Arg1, 1)
if (!player)
{
console_print(id, "Sorry, player %s could not be found or targetted!", Arg1)
return PLUGIN_HANDLED
} else {
set_user_health(player, Health)
}
}
return PLUGIN_HANDLED
}
CVAR
Cvar是服务器端的存储键,例如”mp_startmoney”存储了开局钱数的信息。你可以在plugin_init()里注册自己的Cvar。下面以mp_startmoney的作用为例:
#include <amxmodx>
public plugin_init()
{
register_plugin("CVAR Test", "1.0", "BAILOPAN")
//默认值为800
register_cvar("amx_startmoney", "800")
}
//当玩家进入服务器时触发client_putinserver(Forward)
public client_putinserver(id)
{
if (get_cvar_num("amx_startmoney") > 0)
{
cs_set_user_money(id, get_cvar_num("amx_startmoney"))
} else {
cs_set_user_money(id, get_cvar_num("mp_startmoney"))
}
}
(这个插件可能不会工作,这只是一个演示。)你可以设置浮点、数值或字符串的Cvar,操作时就像HL中本来的Cvar一样简单。
结语
想学习更多的AMXX编程方法,你最好阅览一下各个inc文件中的函数用法。Inc文件通常遵循两个格式:用模块名或用途命名,如果有预先定义的常数或列表,会写在_const中,如果有一些有用的信息或成组的函数,就会写在_stock中。注意只有用到时stock才会被编译,所以#include一些包含很多组stock的inc文件是比较安全的。