[关闭]
@sogouwap 2017-06-06T04:24:03.000000Z 字数 5810 阅读 4977

Lv.5 中级难度 — AMXX插件编写教程


前言

本教程由小灰编写,这是对于 低难度 更上一层教程
建议在对 低难度 学习之后再阅读。
我们的QQ群:139659650


快速切换

点击上述列表中 未打钩 旁边的切换按钮,以切换学习等级。

常见问题

这个教程学习下来需要多少时间?
你需要花一些时间直到你掌握它,并且每天都要坚持编写代码。
根据理解力的不同,需要的时间也不一样(三个月,六个月,或一个星期)

我需要准备什么样的系统?

你需要 Windows 7/8/10 或者 Windows XP 系统。



一、flags状态

我们可以通过实时状态来获取玩家 是否处于 蹲下 , 在地面在水中 等情况

玩家实时状态是我们经常用在插件中进行判断的部分,比如某些情况下,玩家在空中就不能继续执行我们的效果,亦或者玩家蹲着的时候如果按G键使用技能 会提示 在蹲伏状态下不能使用技能,这些都是依靠 pev_flags 来完成的

那究竟是怎么样的代码?

例如我们要判断玩家 是不是 蹲着 可以这样写

  1. if(pev(id, pev_flags) & FL_DUCKING)
  2. {
  3. client_print(id, print_chat, "你是蹲着的")
  4. }

那如果没有蹲着呢?通过之前我们学到的 ! 反义就可以判断了

示例:

  1. if( ! (pev(id, pev_flags) & FL_DUCKING) )
  2. {
  3. client_print(id, print_chat, "你站着的噢~")
  4. }

除了蹲着,还有在空中,在水里,等各种情况 详情可以通过 hlsdk_const.inc 来查看

注意:蹲着不需要你按住CTRL不放。就比如你在管道被上方障碍物抵挡,也属于蹲伏状态。

二、移动类型(Movetype)

不同的实体具有不同的移动类型,移动类型决定了 目标实体是否以 抛物线 或者 直线 移动

移动类型是实体设定的一种物理引擎检测的方式,比如直线飞行抛物线飞行抛物线并且带弹开效果(参考手雷撞击墙壁)光线的折射弹开效果(反射效果)完全不动 等等,这些都属于移动类型。

每个实体几乎都具备上述的其中(可能没有包含所有) 一个条件,比如玩家的移动类型是 MOVETYPE_WALK,手雷是 MOVETYPE_BOUNCE

移动类型有大量返回值,你可以参考下面的 移动类型(MOVETYPE) 表 来查看详情

引用于 >> hlsdk_const.inc

  1. MOVETYPE_NONE = 0 //不移动
  2. MOVETYPE_WALK = 3 //在地上移动(只能用于玩家实体上)
  3. MOVETYPE_STEP = 4 //有重力,特殊边缘处理(用在怪物实体上 HL ?)
  4. MOVETYPE_FLY = 5 // 没有重力,但能和实体碰撞(如果有推力 呈直线飞行)
  5. MOVETYPE_TOSS = 6 //有重力,能和实体碰撞(如果有推力呈 抛物线飞行)
  6. MOVETYPE_PUSH = 7 //no clip to world , push and crush (具体不明)
  7. MOVETYPE_NOCLIP = 8 //没有重力没有碰撞,只移动(穿墙用)
  8. MOVETYPE_FLYMISSILE = 9 //给怪物实体设定额外尺寸(不理解)
  9. MOVETYPE_BOUNCE = 10 //和 MOVETYPE_TOSS 差不多,但会弹起来(TOSS是停在地上)
  10. MOVETYPE_BOUNCEMISSILE = 11 //如光线反射,没有重力的飞行,弹开
  11. MOVETYPE_FOLLOW = 12 //跟随瞄准的实体(需要设置 pev_aiment ; 参考帽子插件)
  12. MOVETYPE_PUSHSTEP = 13 //需要物理效果,世界的碰撞BSP地图模型(用于服务器中的NPC)

示例:

获取是什么移动类型?

  1. pev(id, pev_movetype)

通过上述代码得到的返回值就是了。

设置移动类型?

  1. set_pev(id, pev_movetype, MOVETYPE_NOCLIP) //设定目标id(index)为穿墙的移动方式

三、固体类型(Solid)

不同的固体类型(pev_solid) 决定了实体目标的穿透性

若两个玩家在一起互相碰撞,他们只会撞在一起,而不会穿透过去 这就是Solid的阻挡效果

很多实体都拥有 pev_solid,比如手雷飞出去撞击玩家后会弹开

那到底有哪些呢?和 pev_movetype 移动类型一样,pev_solid 也拥有一些返回值

引用于 >> hlsdk_const.inc

  1. SOLID_NOT = 0 //对其他实体没有任何作用,例如无碰撞
  2. SOLID_TRIGGER = 1 //与实体的边缘发生碰撞效果,但不会挡住实体
  3. SOLID_BBOX = 2 //与实体边缘产生碰撞,并且挡住实体
  4. SOLID_SLIDEBOX = 3 //与实体边缘发生碰撞效果,但被撞击的这个实体必须不在地上
  5. SOLID_BSP = 4 // BSP clip ,与实体的边缘发生碰撞效果,挡住实体 (Clip 是一些手雷或子弹能穿过的透明墙,而玩家不能穿过)

设置或获取的方法 与 pev_movetype 大同小异。

示例:

注意:SOLID_TRIGGER 在某些情况下不能直接设置,不然服务器会崩溃。

四、通过两点获取距离(Distance)

通过获取两个目标的坐标点,我们可以取得他们的间隔距离

要知道距离,我们必须知道两个坐标点位置,坐标点的获取方法可以参考之前的教程

通过 pev_origin 获取两个坐标点之后,接下来要做的就是获取距离

获取距离的代码是 get_distance_f ,该代码有两个参数

  1. 1. 坐标点A
  2. 2. 坐标点B

比如我们取到的两个坐标点分别是 org1org2 就可以这样写:

  1. get_distance_f(org1, org2)

然后他就会通过这两个坐标点自动帮我们算出距离,我们利用这个距离的返回值就可以了

示例:

  1. new Float:iDist = get_distance_f(org1, org2)
  2. client_print(id, print_chat, "两点的距离是: %.0f", iDist)

五、获取/设定角度(Pev_Angle)

我们的视角瞄准角度,或扔出的手雷他飞向哪边 等等,都可通过 pev_angle 来取得方向

通过 pev_angles 可以取得目标角度,该代码也是类似于 pev_origin 的三次数组

示例:

  1. new Float:ang[3]
  2. pev(id, pev_angles, ang)
  3. ang[0] //X轴(上下角度)
  4. ang[1] //Y轴(左右角度)
  5. ang[2] //Z轴(倾斜角度)

注意:我们不能用pev_angles代码直接为玩家设置角度,还需要一个代码:pev_v_angle ,他和 pev_angles 的区别仅仅是上下相反。
另外对于玩家来说还需要用到 pev_fixangles 才能成功修改角度

示例:

  1. new Float:ang[3]
  2. set_pev(id, pev_angles, ang)
  3. set_pev(id, pev_v_angle, ang)
  4. set_pev(id, pev_fixangle, 1)

六、延迟执行函数

在很多情况下,我们需要执行一个带延迟的函数,这种情况下需要用到 set_task 来完成

示例:

  1. set_task(2.0, "headfunc", id)

set_task 拥有三个参数(也有更多 但这里不讲),他们分别如下:

1.延迟的时间(浮点数)
2. 延迟执行什么函数(填写函数名)
3. 延迟执行的触发者(填写索引)

比如我们要用 set_task 延迟 10秒 触发 thefunc 函数 ,可以这样写:

  1. #include <amxmodx>
  2. public plugin_init()
  3. {
  4. register_clcmd("say /test", "testfunc")
  5. }
  6. public testfunc(id)
  7. {
  8. set_task(10.0, "thefunc", id)
  9. }
  10. public thefunc(id)
  11. {
  12. client_print(id, print_chat, "触发测试")
  13. }

七、循环

很多情况下都会用到循环,他可以帮助我们实现不同的效果以及获取更多的数据

在什么情况下会用到循环?

当我们需要通过 AMXX 插件获取玩家人数的时候,或者 需要用循环给所有符合条件的玩家 执行一些命令 就需要用到循环

循环写法(1):杀死所有人

示例如下,创建 i 作为索引 循环 32 个目标

  1. for(new i=1; i<33; i++)

比如说,我们需要输入一个命令然后干掉所有的目标,可以像下面这样写:

  1. #include <amxmodx>
  2. public plugin_init()
  3. {
  4. register_clcmd("say /kill", "kill_all")
  5. }
  6. public kill_all(id)
  7. {
  8. for(new i=1; i<33; i++)
  9. {
  10. if(!is_user_alive(i)) continue
  11. user_kill(i)
  12. }
  13. }

不太理解?

循环主代码为 for

定义首先是 new i = 1,然后用了 i < 33 来表达需要循环的次数,其中 33 代表玩家数量(预留一位)

在循环中, 代码 continue 是用来 忽略该目标,比如说找到了一个 死亡的玩家,那么如果用 continue 来判断的话,循环不会终止,而只是会忽略那个判断到的目标,而继续执行循环

其中,我们新建了一个叫 i 的索引,他是用来存储我们循环到的目标的,比如说通过循环找到的目标 如果定义为 i 那么我们随时都可以使用这个新的索引来帮助我们执行一些效果

代码中 user_kill 是杀死玩家的函数(amxmodx.inc),这里填写的索引就是 i 而不是 id (不然你只会把自己杀死)

循环写法(2):获取指定目标的数量

有时候我们可能会遇到,获取目标数量的问题,我们同样可以用到循环来解决问题

示例:

  1. #include <amxmodx>
  2. public plugin_init()
  3. {
  4. register_clcmd("say /get", "get_playernumber")
  5. }
  6. public get_playernumber(id)
  7. {
  8. new xp = 0
  9. for(new i=1 ; i<33; i++)
  10. {
  11. if(!is_user_alive(i)) continue
  12. xp += 1
  13. }
  14. client_print(id, print_chat, "存活的玩家数量是 : %d 个", xp)
  15. }

没有看懂?

和上一个例子几乎一样,不过这里添加了一个临时变量 xp,这个临时变量是用来充当计数器的,每次循环的时候 continue 会阻止不符合条件的目标,然后会触发 xp += 1

这个时候,xp变量中的值就会随着循环到的目标数量而增加,然后我们用 client_print 再调用 xp 临时变量的值 就可以显示了

八、改变玩家重力

在某些情况下,我们可能需要改变玩家的重力,越轻跳得越高

实现的代码为 pev_gravity (PEV函数)

示例:修改玩家重力为200.0

  1. set_pev(id, pev_gravity, 200.0 / 800.0)

不太明白?

虽然看出 200.0 是重力值,但是后面的 800.0 是什么?

我们需要通过 800.0 需要算一个值,对于 pev_gravity 来说,800.0 就是 1.0,所以 我们的 200.0 / 800.0 也就是 200 除以 800.0 ,该计算出的值就是最终结果

为什么是800.0?

不,并不是 800.0 ,这个 800.0 是取决于 服务器参数 sv_gravity

所以如果我们要实现一个完美的重力设置,需要这样写:

写法(1):用变量分开再最终组合

  1. new Float:setgravity = 200.0
  2. new Float:getserver = get_cvar_float("sv_gravity")
  3. set_pev(id, pev_gravity, setgravity / getserver)

写法(2):直接组合

  1. set_pev(id, pev_gravity, 200.0 / get_cvar_float("sv_gravity"))

以上这两种方法都可以实现 同一个目的

注意:代码中的 / 是计算方式的 除以

九、打断数据(基础)

有些情况下,我们需要打断原有的数据

比如:我们按G键可以丢弃一把武器,但是我们不希望他被丢弃

这种情况下,我们需要打断原有数据

G键的默认命令是 drop 也就是丢弃武器。我们可以 HOOK(勾住) 这个命令以实现我们的目的

这里我们用 register_clcmd 就可以了,示例如下:

  1. #include <amxmodx>
  2. register_clcmd("drop", "dropfunc")
  3. public dropfunc(id)
  4. {
  5. client_print(id, print_center, "你触发了丢弃命令。")
  6. }

可是这样不能达成我们的目的,武器依然被丢弃了,我们现在只是可以在 按[G]键 的时候提示一下信息

所以,我们需要打断原有数据,让其不丢弃武器

这里我们需要用到 返回,代码为 return ,但是 我们用到的不是一般的返回,而是带有特定返回值的返回

打断这种丢弃数据需要用到的返回值是 PLUGIN_HANDLED

配合返回代码 我们组合后可以写出这样:

  1. return PLUGIN_HANDLED

这就是我们需要的打断效果。通过组合后我们可以得到这样的代码:

  1. #include <amxmodx>
  2. public plugin_init()
  3. {
  4. register_clcmd("drop", "dropfunc")
  5. }
  6. public dropfunc(id)
  7. {
  8. client_print(id, print_center, "你无法丢弃武器。")
  9. return PLUGIN_HANDLED
  10. }

编译后再测试的话就是这样的效果了:

此处输入图片的描述

注意:
通过 drop 这个命令进行hook,再配合返回值 PLUGIN_HANDLED 我们可以完全阻断丢弃武器的事件,但如果玩家死亡的时候,武器依然会掉落

Level.5 中难度 教程结束

你可以查看 低难度 (上一节) / 升级难度(下一节)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注