前言

我们这一章节将会再深入了解amxx插件的开发,了解玩家属性状态、固体、距离、角度,以及阻断玩家命令等内容。有疑问可以在文章底部留言。

flags状态

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

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

那究竟是怎么样的代码?

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

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

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

示例:

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

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

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

Movetype移动类型

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

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

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

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

引用于 >> hlsdk_const.inc

1
2
3
4
5
6
7
8
9
10
11
12
MOVETYPE_NONE = 0 //不移动
MOVETYPE_WALK = 3 //在地上移动(只能用于玩家实体上)
MOVETYPE_STEP = 4 //有重力,特殊边缘处理(用在怪物实体上 HL ?)
MOVETYPE_FLY = 5 // 没有重力,但能和实体碰撞(如果有推力 呈直线飞行)
MOVETYPE_TOSS = 6 //有重力,能和实体碰撞(如果有推力呈 抛物线飞行)
MOVETYPE_PUSH = 7 //no clip to world , push and crush (具体不明)
MOVETYPE_NOCLIP = 8 //没有重力没有碰撞,只移动(穿墙用)
MOVETYPE_FLYMISSILE = 9 //给怪物实体设定额外尺寸(不理解)
MOVETYPE_BOUNCE = 10 //和 MOVETYPE_TOSS 差不多,但会弹起来(TOSS是停在地上)
MOVETYPE_BOUNCEMISSILE = 11 //如光线反射,没有重力的飞行,弹开
MOVETYPE_FOLLOW = 12 //跟随瞄准的实体(需要设置 pev_aiment ; 参考帽子插件)
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
2
3
4
5
SOLID_NOT = 0 //对其他实体没有任何作用,例如无碰撞
SOLID_TRIGGER = 1 //与实体的边缘发生碰撞效果,但不会挡住实体
SOLID_BBOX = 2 //与实体边缘产生碰撞,并且挡住实体
SOLID_SLIDEBOX = 3 //与实体边缘发生碰撞效果,但被撞击的这个实体必须不在地上
SOLID_BSP = 4 // BSP clip ,与实体的边缘发生碰撞效果,挡住实体 (Clip 是一些手雷或子弹能穿过的透明墙,而玩家不能穿过)

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

示例:

  • 获取:pev(id, pev_solid)
  • 设置:set_pev(id, pev_solid, SOLID_BBOX)

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

Distance通过两点获取距离

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

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

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

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

  • 坐标点A
  • 坐标点B

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

1
get_distance_f(org1, org2)

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

示例:

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

Pev_Angle获取/设定角度

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

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

示例:

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

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

示例:

1
2
3
4
new Float:ang[3]
set_pev(id, pev_angles, ang)
set_pev(id, pev_v_angle, ang)
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
2
3
4
5
6
7
8
9
10
11
12
13
#include <amxmodx>
public plugin_init()
{
register_clcmd("say /test", "testfunc")
}
public testfunc(id)
{
set_task(10.0, "thefunc", id)
}
public thefunc(id)
{
client_print(id, print_chat, "触发测试")
}

循环

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

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

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

循环写法1:杀死所有人

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

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

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

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

不太理解?

循环主代码为for

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

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

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

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

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

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

示例:

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

没有看懂?

和上一个例子几乎一样,不过这里添加了一个临时变量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
2
3
new Float:setgravity = 200.0
new Float:getserver = get_cvar_float("sv_gravity")
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
2
3
4
5
6
#include <amxmodx>
register_clcmd("drop", "dropfunc")
public dropfunc(id)
{
client_print(id, print_center, "你触发了丢弃命令。")
}

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

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

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

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

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

1
return PLUGIN_HANDLED

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

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

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

编译后再测试效果吧。

参考文献

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