搜索
查看: 6308|回复: 10

【游途道标】【原创】【AMXX编程入门系列】【一】

[复制链接]
发表于 2007-2-16 22:04:54 | 显示全部楼层 |阅读模式 来自 中国–福建–泉州
春节假期闲来无事,搞了个CS1.6版服务器,玩了两把感到无味,经人介绍上dcoo,被其插件所迷,故,苦寻架设之法以效之。上下两天有余,所寻插件功能尚感欠缺,得闻其件开源,吾心暗喜,看其源改其码,两日之内亦有所心得,不敢独享,故,发此系列以报。
——FHB13(游途道标)2007年2月16日

========================================
本人不想以枯燥的函数说明来带大家入门,而用具体事例带各位入门。本系列第一篇就用一个比较实用而且短小的源码来做讲解。具体源码如下(讲解在后头):



[php]
#include <amxmodx>
#include <cstrike>
new p_lastk=0,p_lastk_count=0
public plugin_init() {

register_plugin("MultiKill", "1.0", "Marshall")
register_event("DeathMsg", "hook_death", "a")
register_logevent("hook_roundstart",2,"0=World triggered","1=Round_Start")
}
public hook_death(){

new p_message[128]
new Killer = read_data(1)
new headshot = read_data(3)
new p_weapon[16]
read_data(4,p_weapon,15)

//爆头奖励
if(headshot){
new p_name[16]
get_user_name(Killer,p_name,15)
format(p_message,127,"^x04爆头奖励:^x03%s^x01 获得$1000!",p_name)
color_message(p_message)
cs_set_user_money(Killer,cs_get_user_money(Killer) + 1000)
}
//刀杀奖励
if(strcmp(p_weapon,"knife")==0){
new p_name[16]
get_user_name(Killer,p_name,15)
format(p_message,127,"^x04刀杀奖励:^x03%s^x01 获得$1500!",p_name)
color_message(p_message)
cs_set_user_money(Killer,cs_get_user_money(Killer) + 1500)
}

//雷杀奖励
if(strcmp(p_weapon,"grenade")==0){
new p_name[16]
get_user_name(Killer,p_name,15)
format(p_message,127,"^x04雷杀奖励:^x03%s^x01 获得$1500!",p_name)
color_message(p_message)
cs_set_user_money(Killer,cs_get_user_money(Killer) + 1500)
}

//连续杀人奖励
if(p_lastk != Killer){
p_lastk = Killer
p_lastk_count =1
}
else
p_lastk_count++

if(p_lastk_count>=5){
new p_name[16]
get_user_name(Killer,p_name,15)
format(p_message,127,"^x04连续杀人奖励:^x03%s^x01 连续杀人5次,获得$3000!",p_name)
color_message(p_message)
cs_set_user_money(Killer,cs_get_user_money(Killer) + 3000)
}
}
public hook_roundstart(){
p_lastk=0
p_lastk_count=0
}
color_message(p_message[]){
message_begin(MSG_ALL, get_user_msgid("SayText"))
write_byte(1)
write_string(p_message)
message_end()
}
/* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE
*{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang2052\\ f0\\ fs16 \n\\ par }
*/
[/php]
分析部分
首先先看开头部分如下代码
[php]#include <amxmodx>
#include <cstrike>[/php]
这两行的意思是把#include后面“<”和“>”所包含的amxmodx和cstrike两个文件在编译的时候,自动把该文件里的内容加到这个文件(源文件)前面。
要说明几点:
1、其实amxmodx和cstrike指的是amxmodx.inc和cstrike.inc,只是把后面的.inc省略了。
2、“<”和“>”所包含的文件,表示该文件在编译器所在文件夹下的“include”子文件中。


3、用“”(双引号)所包含的文件,表示该文件在和你源代码相同的目录中。
如#include ”cstrike.inc”表明cstrike.inc不在编译器所在文件夹下的“include”子文件中,而是在和你源代码相同的目录中。


4、前面说的编译器自动把该文件里的内容加到这个文件(源文件)前面,可是你也可以手动,方法就是找到#include所包含的那个文件并打开,复制里面的所有文本内容,然后粘贴到你源代码开头,这样做的效果和用#include包含文件的效果一样,当然了,你手动这样做#include就要去掉,不用再包含了,而且你这样做你源代码会很长不好看。



5、#include一定要放在最开头。


6、具体插件因该加载什么文件,这个要根据你调用的函数而定,你可以到AMXX说明文档里面查。
如“callfunc_push_float()”这个函数,我在说明文档查到其说明如下:
callfunc_push_float
Core (amxmodx.inc)

Description
callfunc_push_float - Pushes a floating point number to a function call.

Syntax
callfunc_push_float ( Float: value )
等等……
其中我用红色表示出来的部分就是调用这个函数要加载的文件。



7、相同文件只须加载一次。

什么是函数
我们接下来看看下面这个段代码:


[php]public plugin_init() {[/php]
[php]

register_plugin("MultiKill", "1.0", "Marshall")
register_event("DeathMsg", "hook_death", "a")
register_logevent("hook_roundstart",2,"0=World triggered","1=Round_Start")
}
[/php]

上面这段代码,就是一个完整的函数,具体函数的定义如下:
public 函数名(参数列表){
代码1
代码2
……
代码n
}
如上,这里说明下:
1、public这个表示是否对外公开调用(我猜测的),加上表示游戏可以直接调用这个函数,不加上表示游戏不能调用,要通过其他函数调用,而且调用的函数必须和被调用的那个函数在同一个文件里。
2、函数名,除了一定要以字母开头不能重名以外,自己取。用中文名我没试过,你可以试试。
3、参数列表,有就填,没有就留空。后面碰到有参数的函数我再介绍。
3、代码1——代码n没什么好说的。
4、{}表示一个函数的开始和结束。


现在后过头看“plugin_init()“这个函数,这个函数比较特殊,每个插件必须有一个这样的同名函数,而且只能一个。当你插件被加载,第一个开始执行的地方就是这个函数,也就是说,你插件初始化的部分就在这个函数里面,而且每个这个函数你的插件更本不能被执行。
这个函数加了public声明,主要是为了让系统调用。而且你还可以发现这个函数没有参数,就是函数名后面扣号是空的。


再看这个函数的第一条语句:
register_plugin("MultiKill", "1.0", "Marshall")
这条语句属于一个函数调用,其本身就是一个函数,而且有三个参数,调用的时候只要写函数名加扣号加参数如果有的话。
register_plugin函数原形如下:
register_plugin ( const plugin_name[], const version[], const author[] )
你可以在AMXX文档里查到具体说明,我这里简单说下:
1、声明不同于定义,定义是用来写函数具体实现部分如上面的plugin_init函数,声明是用来表示一个函数因该如何调用的。
如这个插件的plugin_init函数的声明就是:


plugin_init()


定义就是上面给出来的那些代码如下:

public plugin_init() {



register_plugin("MultiKill", "1.0", "Marshall")
register_event("DeathMsg", "hook_death", "a")
register_logevent("hook_roundstart",2,"0=World triggered","1=Round_Start")
}


我们看register_plugin的函数声明就是可以知道这个函数有三个参数,函数的参数是用逗号隔开的,其他符号不是函数的分割符,切记。

再看参数以const开头表示,这个函数调用这个参数时,不会改变这个参数的数值和内容。
参数以[]结尾表示这个参数必须是一个数组。
我在这里介绍下什么是字符串:
1、字符指的是单个字符,而且必须用单引号扣起来,如'1','2','a','d'等等。
2、字符串,故名思义就是一串字符,必须用双引号扣起来,如"12345","a12ad","1afbsd3"等等。
3、当然了,"a"这个表示字符串,'a'这个表示字符,两者初看出了引号不同没什么不同,但是C风格的字符串要求以0结尾,也就是说"a"会被编译器翻译成61h 00h存放在文件中,而'a'只会被翻译成61h少了一个00h。数值后面的h表示16进制数。
说了一大堆我再回来介绍下,register_plugin函数各个参数的意义,估计你已经看出来了。
第一个参数是你要为这个插件取的名字
第二个表示你要为这个参件设置的版本
第三个表示你要为这个参件设置的作者
如你可以上面那段代码改成这个样子:
register_plugin("我的第一个插件", "版本7.1.4", "游途道标")
上面这段代码的意思是告诉register_plugin函数去帮你吧你写的这个插件的名字改成"我的第一个插件",把版本改成"版本7.1.4",把编写者改成"游途道标"。
你只要通过参数告诉函数希望改成什么数值就好,函数会自动去帮你完成的。


接下来继续看下面这个函数:
register_event("DeathMsg", "hook_death", "a")
他的原形如下:
register_event ( const event[], const function[], const flags[], [ cond=[], ... ] )
你可以看到原形有4个参数,调用只调用了3个。当然是他省略了一个,省略参数只能从后往前,不能从前往后或跳着省略。而且省略参数函数必须提供默认值。
这个函数是用来注册事件的,这个比较关键。整个函数的关键在第二个参数,第二个参数必须是你自己定义的一个函数名,该函数的意思是,如果游戏满足了第一、三、四的条件后会自动去调用你的第二个参数指定的那个函数。
这里指定的函数是hook_death,你可以在这个源码里找到,这种有系统自己调用而不是你亲自调用的函数称为回调函数。C规定,函数名代表函数的指针,也就是这个函数第一条指令的地址。


再下看一个函数:
register_logevent("hook_roundstart",2,"0=World triggered","1=Round_Start")
功能和上面的那个类似,其原形如下:
register_logevent ( const function[], argsnum, ... )
原形中的省略号是指这个函数是不定参数个数的函数,其中必须要有两个以上参数。
最关键的是第一个,他和上面一样,只要满足后面几个参数的条件游戏就自动调用第一个参数指名的函数。这里指名的是hook_roundstart函数。


全局和局部变量
[php]new p_lastk=0,p_lastk_count=0[/php]
现在回过头看上面的这行代码,new是用来声明变量的,后面跟着变量名和初始值。多个变量用逗号隔开。
你可以看到这个变量在所有函数外面声明,和下面的这段后面代码声明的地方有所不同。


[php]public hook_death(){[/php]
[php]

new p_message[128]
new Killer = read_data(1)
new headshot = read_data(3)
new p_weapon[16]
read_data(4,p_weapon,15)
[/php]

这几个在函数里面。

我们称,函数外面声明的变量叫全局变量,里面的叫局部变量。
他们不光名称不同,功能也不同。
全局变量是所有函数都可以看到和修改的,而且从插件启动到结束其值只随插件内部对其的修改而改变,局部变量不一样,函数没被执行一次就要被从新创建一次,函数一结束也会随着消失,全局变量创建时必须初始化,如果不初始化默认的值就是0,局部变量不能初始化,只能创建完成后再重新赋值给他,他默认的值是随机数。
而且局部变量只能被所创建它的函数调用。

if语句
[PHP]
if(p_lastk != Killer){
  p_lastk = Killer
  p_lastk_count =1
}
else
  p_lastk_count++
[/PHP]
如上这段代码就是一个if语句,它是一个判断语句。
if()他不是一个函数,口号里面放着的不是参数,是一个表达式。
p_lastk != Killer表示p_lastk不等于Killer,其他符号还有如下:
==等于
!=不等于
<=大于等于
>=小于等于
<大于
>小于
其中这个表达式计算完后回产生一个布尔值,0表示不成立,1表示成立。C语言规定大于1的数都是成立的意思。
if语句的原形如下:

if (条件表达式)
  {
    代码1
    代码2
    ……
    代码n
  }
else
  {
    代码1
    代码2
    ……
    代码n
  }

如果只要一条语句{}可以省略,如果else没有语句可以省略。如前面那个代码,如果p_lastk != Killer成立则执行后续部分的语句,跳过else后面的语句,如果不成立则跳过后续语句,到else接下去执行。

最后说明下:
1、if(strcmp(p_weapon,"grenade")==0)这条语句的意识是,调用strcmp函数比较p_weapon所存放的字符串与"grenade"字符串比较是否相等,相等则返回0,然后再与==后面的那个0比较,看看strcmp函数返回的是不是真是0。如果是则为真,不是则为假。
2、我还可以写出这样的表达式来if(Killer),他的意识是,只要Killer不为0就成立,为真。
3、函数的参数和调用的参数可以不同明,具体说明如下:

public 函数1()
{
new killer
代码1
代码2
……
函数2(Killer)
……
代码N
}

public 函数2(id)
{
new user
代码1
代码2
……
user = id
……
代码N
}

这里函数1调用函数2时用的参数名士Killer但是在函数2里面变成了id,这里要说明的是,函数1调用函数2把killer的值传给了函数2,并赋给了id,而且这个id不必声明,可以直接在函数2里面使用,而且函数2不能使用函数1种Killer这个变量名来操作,只能用id。

现在你应该可以独立看一些插件源码了,刚开始有点困难,看多了就好了,都是这样过来的,谁都有当菜鸟的时候,关键是你想当多久而已。希望这篇文章对你有所帮助,本来想写简单一点,谁知道一写知识面牵扯的有点广,后面一直想收手可惜事与愿违,最后只能来硬的。
发表于 2007-2-18 18:56:22 | 显示全部楼层 来自 中国–四川–宜宾

回复: 【游途道标】【原创】【AMXX编程入门系列】【一】

顶!希望继续努力,带领更多的菜鸟入门!
回复

使用道具 举报

发表于 2007-2-20 23:24:40 | 显示全部楼层 来自 中国–广西–柳州–鱼峰区

回复: 【游途道标】【原创】【AMXX编程入门系列】【一】

不错不错,可俺还是不懂,看得头都大了,老兄可否写一个游戏玩家依名次给与头衔的插件出来啊,比如加入头衔字段,
amx_setlevelinfo "【上尉】" "10"
amx_setlevelinfo "【中尉】" "50"
amx_setlevelinfo "【列长】" "100"
amx_setlevelinfo "【小兵】" "150"
进入服显示和第一视角显示的。找了N久了都没找到。有劳。
回复

使用道具 举报

发表于 2007-2-23 15:42:36 | 显示全部楼层 来自 中国–广东–中山

回复: 【游途道标】【原创】【AMXX编程入门系列】【一】

非常好!
简直是雪中送炭啊!
希望DT能提供更多这样的教程!
回复

使用道具 举报

发表于 2007-2-25 00:26:11 | 显示全部楼层 来自 中国–浙江–湖州

回复: 【游途道标】【原创】【AMXX编程入门系列】【一】

对我平常改插件大有帮助啊
回复

使用道具 举报

发表于 2010-4-23 17:40:09 | 显示全部楼层 来自 中国–四川
非常好的贴子
回复

使用道具 举报

发表于 2010-4-25 19:43:57 | 显示全部楼层 来自 中国–重庆–重庆
好教程   学习了
回复

使用道具 举报

发表于 2010-4-26 12:40:01 | 显示全部楼层 来自 中国–天津–天津
同意6楼的观点,想学插件的朋友要看的第一贴,看懂这个是第一步,如果这个帖子都看不懂我劝你放弃,如果看懂了,就要把这个帖子中的话牢记于心,是以后所有的基础
回复

使用道具 举报

发表于 2010-6-29 10:54:35 | 显示全部楼层 来自 中国–湖北–黄冈–麻城市
好帖子  顶下
回复

使用道具 举报

发表于 2010-7-15 17:48:15 | 显示全部楼层 来自 中国–重庆–重庆
我也顶//。//
回复

使用道具 举报

游客
回复
您需要登录后才可以回帖 登录 | 注个册吧

快速回复 返回顶部 返回列表