@sogouwap
2017-06-04T17:12:07.000000Z
字数 10534
阅读 7136
前言
本教程由小灰编写,这是
进阶
难度的教程
建议在对普通难度学习之后再阅读
我们的QQ群:139659650
点击上述列表中
未打钩
旁边的切换按钮,以切换学习等级。
这个教程学习下来需要多少时间?
你需要花一些时间直到你掌握它,并且每天都要坚持编写代码。
根据理解力的不同,需要的时间也不一样(三个月,六个月,或一个星期)
我需要准备什么样的系统?
你需要 Windows 7/8/10 或者 Windows XP 系统。
在 FakeMeta
模块中有一种代码叫 Pev
pev是一个神奇的功能,他几乎可以篡改游戏中的任何数据,比如 生命值
,重力
,固体类型
,移动类型
,摩擦力
,坐标位置
等等…
我们可以通过 `Pev` 来获取或者更改游戏中的数据
例如:
生命值是 pev_health
重力是 pev_gravity
坐标系是 pev_origin
移动向量 pev_velocity
让我们来看看如何用 Pev
更改以及获取
#include <amxmodx>
#include <fakemeta>
public test(id)
{
set_pev(id, pev_health, 140.0)
}
#include <amxmodx>
#include <fakemeta>
public test(id)
{
new Float:org[3]
pev(id, pev_origin, org)
client_print(id, print_chat, "玩家坐标是: [X: %.2f], [Y: %.2f], [%.2f]", org[0], org[1], org[2])
}
400.0
的速度向上飞起)
#include <amxmodx>
#include <fakemeta>
public test(id)
{
new Float:vel[3]
vel[2] = 400.0
set_pev(id, pev_velocity, vec)
}
不难看出,获取也就是只写 `pev` ,而修改则需要写上 `set_pev` 。这就是他们的区别
那
pev类型
到底有哪些呢?(下面的代码摘于:百度贴吧)
2.1.1 字符串型实体属性 里面装载另一个字符串
pev_string_start = 0, //无效的实体属性 主要用于遍历
pev_classname, 实体类型名称 做地图的人会知道这些 实体的类决定了实体是什么用途
pev_globalname,
pev_model, 实体模型
pev_target, 实体的目标 用于控制
pev_targetname, 实体的目标 用于控制
pev_netname, 实体的网络名称 如玩家游戏名称
pev_message, 实体的激发的文字
pev_noise, 保留备用的 可以自由存放
pev_noise1, 保留备用的 可以自由存放
pev_noise2, 保留备用的 可以自由存放
pev_noise3, 保留备用的 可以自由存放
pev_string_end, //无效的实体属性 主要用于遍历
2.1.2 实体值型实体属性 里面装载另一个实体号
pev_edict_start, //无效的实体属性 主要用于遍历
pev_chain, //不明
pev_dmg_inflictor, //被伤害的人/实体 ,攻击者
pev_enemy, //敌人 用于NPC
pev_aiment, //帽子等装饰 用于连接2个实体
pev_owner, //所有者
pev_groundentity, //脚下的实体
pev_euser1, //保留备用的 可以自由存放
pev_euser2, //保留备用的 可以自由存放
pev_euser3, //保留备用的 可以自由存放
pev_euser4, //保留备用的 可以自由存放
pev_edict_end, //无效的实体属性 主要用于遍历
2.1.3 小数型(浮点)实体属性 取得或设置实体的详细值
pev_float_start, //无效的实体属性 主要用于遍历
pev_impacttime, //碰撞时间
pev_starttime, //开始时间
pev_idealpitch, //理想的PITCH范围
pev_ideal_yaw, //理想的YAW范围
pev_pitch_speed, //朝向PITCH的速度
pev_yaw_speed, //朝向YAW的速度
pev_ltime, //时间长度
pev_nextthink, //下次心跳时间
pev_gravity, //重力
pev_friction, //摩擦速度 如滑雪
pev_frame, //当前实体播放的动作帧数
pev_animtime, //在什么时候开始播放实体动画
pev_framerate, //动画播放速度比率
pev_scale, //大小 仅用于SPR
pev_renderamt, //渲染 详见 fakemeta_util.inc fm_set_rendering
pev_health, //生命值
pev_frags, //比分
pev_takedamage, //实体是否可被伤害 参看HLSDK.INC
pev_max_health, //最大生命值
pev_teleport_time, //传输时间
pev_armortype, //护甲类型
pev_armorvalue, //护甲值
pev_dmg_take, //伤害值保存
pev_dmg_save, //伤害值保存
pev_dmg, //实体威力值 用于手雷等爆炸物
pev_dmgtime, //实体威力爆发时间 主要用于手雷爆炸物
pev_speed, //速度
pev_air_finished,
pev_pain_finished,
pev_radsuit_finished,
pev_maxspeed, //最大速度
pev_fov, //视野
pev_flFallVelocity, //坠落的速度
pev_fuser1, //保留备用的 可以自由存放
pev_fuser2, //保留备用的 可以自由存放
pev_fuser3, //保留备用的 可以自由存放
pev_fuser4, //保留备用的 可以自由存放
pev_float_end, //无效的实体属性 主要用于遍历
2.1.4 整数型实体属性 取得或设置实体的详细值
pev_int_start, //无效的实体属性 主要用于遍历
pev_fixangle, //朝向与视角朝向同步
pev_modelindex, //模型缓存序号
pev_viewmodel, //V模型
pev_weaponmodel, //P模型
pev_movetype, //移动类型 参看HLSDK.INC
pev_solid, //固态类型 参看HLSDK.INC
pev_skin, //模型的皮肤样式 如果模型有
pev_body, //子模型 如果模型有
pev_effects, //渲染亮度
pev_light_level, //亮度等级
pev_sequence, //模型的动作序列
pev_gaitsequence, //下半身动作序列 仅用于玩家
pev_rendermode, //详见 fakemeta_util.inc fm_set_rendering
pev_renderfx, //详见 fakemeta_util.inc fm_set_rendering
pev_weapons, //武器表(位)
pev_deadflag, //死亡标记 参看HLSDK.INC
pev_button, //正在被按下的按键
pev_impulse, //手电 喷图 喷射 等
pev_spawnflags, //出生标记 参看HLSDK.INC
pev_flags, //状态标记 参看HLSDK.INC
pev_colormap,
pev_team, //队伍
pev_waterlevel, //浸泡在水中的程度
pev_watertype, //浸泡在哪种液体中
pev_playerclass, //玩家角色
pev_weaponanim, //武器动作
pev_pushmsec, //推时间
pev_bInDuck, //蹲下
pev_flTimeStepSound, //脚步声
pev_flSwimTime, //游泳时间
pev_flDuckTime, //蹲下时间
pev_iStepLeft, //步伐
pev_gamestate, //游戏状态
pev_oldbuttons, //之前按下的按键
pev_groupinfo, //组类型
pev_iuser1, //保留备用的 可以自由存放
pev_iuser2, //保留备用的 可以自由存放
pev_iuser3, //保留备用的 可以自由存放
pev_iuser4, //保留备用的 可以自由存放
pev_int_end, //无效的实体属性 主要用于遍历
2.1.5 向量实体属性 取得或设置实体的向量(数组)
pev_vecarray_start, //无效的实体属性 主要用于遍历
pev_origin, //实体坐标
pev_oldorigin, //实体之前的坐标
pev_velocity, //实体速率
pev_basevelocity, //实体基础速率 用于传送带等
pev_clbasevelocity,
pev_movedir, //可能是移动目标
pev_angles, //实体朝向
pev_avelocity, //实体受速率影响时朝向变化 仅对实体有效
pev_v_angle, //实体视角朝向
pev_endpos, //开始点
pev_startpos, //结束点
pev_absmin, //实体范围 开始
pev_absmax, //实体范围 结束
pev_mins, //实体大小 开始
pev_maxs, //实体大小 结束
pev_size, //实体大小
pev_rendercolor, //详见 fakemeta_util.inc fm_set_rendering
pev_view_ofs, //瞄准视角的速率
pev_vuser1, //保留备用的 可以自由存放
pev_vuser2, //保留备用的 可以自由存放
pev_vuser3, //保留备用的 可以自由存放
pev_vuser4, //保留备用的 可以自由存放
pev_punchangle, //晃动朝向 仅对玩家有效
pev_vecarray_end, //无效的实体属性 主要用于遍历
可能不是所有的
pev类
都包含在里面。标准大全可参考fakemeta_const.inc
我们可以 勾住(Hook)
一些事件,这些事件在游戏中触发的时候,会调用我们勾住的函数,接下来我们可以从篡改或者打断一些效果。
我们可以勾住这些事件:
复活
, 死亡
,切换武器
,造成伤害
,思考
,碰撞
,重新复活
等等…
更多勾住方式可以参考
ham_const.inc
注册Ham事件的方法: RegisterHam( Ham事件, "勾住什么实体", "调用哪个函数名" , Pre / Post )
RegisterHam
有四个参数供我们填写,每个参数需要用 ,
逗号 来逐个隔开,下面我们来看看实例:
public plugin_init()
{
RegisterHam(Ham_Spawn, "player", "PlayerSpawn", 0)
}
我们逐个来解析:
参数1 :
Ham_Spawn
参数2 :"player"
参数3 :"PlayerSpawn"
参数4 :0
这些对应什么呢?
1) 参数1 代表注册 复活
事件,代码为 Ham_Spawn。(更多Ham事件可参考 ham_const.inc
)
2) 参数2 "player"
,这个部分是注册需要勾住什么实体,玩家的实体名是 player
(需用 ""
包含起来)
3) 参数3 "PlayerSpawn"
,这个部分其实就我们需要触发什么函数,填写的是 触发的函数名
,函数里的代码由我们自己定义 (需用 ""
包含起来)
4) 参数4:这里涉及到 先(Pre)
/ 后(Post)
部分,其中0代表Pre,1代表Post
。Ham函数大部分都带有先后顺序,比如对于复活
来说,Pre即复活前
,Post即复活后
。
那整个代码应该是怎么样的呢?
#include <amxmodx>
#include <hamsandwich>
public plugin_init()
{
RegisterHam(Ham_Spawn, "player", "PlayerSpawn", 1)
}
public PlayerSpawn(id)
{
}
- 参数4 如果不填写,默认是Pre(即
0
)- 有一些Ham事件其中的
index(索引)
不止一个- 上例中的复活触发函数应该勾住
复活之后
即 Post
如果索引不止一个呢,代码就会变成下面这样
public test(id, mode)
{
}
那这种一般是怎么使用?
这个方法叫多重传导,一般由两种数据组成 ,如下代码:
test(id, 2)
public test(id, mode)
{
client_print(id, print_chat, "Mode的值是 %d", mode)
}
如上, 触发了 test
函数,其中传递了 参数2
(示例中为 数字2
),然后 test
函数会通过 client_print
打印 mode
的值出来,也就是会显示:
Mode的值是: 2
对于Ham来说有许多事件都拥有 多个参数
, 比如 死亡函数 Ham_Killed
,他拥有三个参数,分别是:
> 受伤者(被杀死的人)
> 攻击者(凶手)
> 尸体碎片
如果要用函数表达出来就是这样:
public PlayerKilled(victim, attacker)
{
}
小提示:
参数3
可以不填,默认就是0
配合Ham注册之后,整个代码就是这样:
#include <amxmodx>
#include <hamsandwich>
public plugin_init()
{
RegisterHam(Ham_Killed, "player", "PlayerKilled", 1)
}
public PlayerKilled(victim, attacker)
{
}
提示:victim 和 attacker 可以按你自己的意图写上去,并不需要完全
照着抄
我们可以在游戏中获取一些信息然后存储下来,比如
玩家名字
或 某些字符串
信息
比如我们需要获取玩家名字,这里我们需要用到的是 fakemeta
模块中的 pev_netname
我们来细致讲一下,首先我们需要知道要获取谁。通过当前已知条件可以写出这样的代码:
pev(id, pev_netname)
但这样是没有作用的,因为获取之后没有保存下来。那么我们应该如何保存
呢
我们来看个代码例句:(获取玩家名字)
public test(id)
{
new fname[33]
pev(id, pev_netname, fname, 32)
client_print(id, print_chat, "玩家 %s 引发了函数", fname)
}
上面的例子不难看出,获取后保存的地方就是 fname
这个数组里面,当我们使用的时候 只需要写上 fname
即可。
那具体是怎么样获取的?
我们首先要分解一下代码。然后我们逐步来分析;
new fname[33]
创建一个临时变量(变量名是 fname
这是一个带数组的存储器,最大存储33字符)id
指的是索引(index),意思是获取谁的名字,要和函数的 索引(index)
对应pev( )
通过 fakemeta
的 PEV 来获取一些数据,在括号 ()
里填写需要获取什么pev_netname
获取玩家的游戏名字(可以通过 fakemeta_const.inc
以查看更多)fname
当我们获取后,将获取到的数据存储到什么地方(这里填写 数组的变量名
)32
我们定义的 数组最大是 33
的存储空间,所以在最后就必须 少一位
。当我们把所有代码组合一下,就变成如下:
new fname[33]
pev(id, pev_netname, fname, 32)
TIP:获取到的名字已经被存储到了
fname
变量中,你可以随时来调用他
当然除了获取名字,还有更多的变通方法
写法注意:创建的临时变量名,要和 pev( ) 里面的那个进行对应。
我们来看看还有哪些其他的使用方式
new class[33]
pev(id, pev_classname, class, 32)
new jmodel[33]
pev(id, pev_model, model, 32)
更多的
pev_
可参考fakemeta_const.inc
* 使用的变量存储
是临时变量
本次我们要学到的是 给予指定装备,简单说就是给玩家特定的 武器
本次我们要用到一个新的模块INC,他是 fakemeta_util.inc
他会给我们带来很多快捷方便的 stock(自定义函数)
,在以后我们也会经常用到。
首先定义模块代码
#include <amxmodx>
#include <fakemeta_util> //这里是新的INC
我们本次需要用到的是 fakemeta_util
中提供的 fm_give_item
这串代码
他的使用方法很简单 ,比如我们需要给玩家一个 AK47 ,代码即:
fm_give_item(id, "weapon_ak47")
这次和之前的 CSW_ 有点区别,但是区别不大,只是将
CSW_
变成了weapon_
现在尝试一下将:之前学到的 获取名字
在配合本次学到的 给予武器
将代码写出来
示例:
#include <amxmodx>
#include <fakemeta_util>
public plugin_init()
{
regsiter_clcmd("say /giveak", "giveak_func")
}
public giveak_func(id)
{
if(!is_user_alive(id)) return //如果玩家死亡 返回
new fname[33]
pev(id, pev_netname, fname, 32) //获取名字 并存储到 fname变量中
client_print(id, print_chat, "玩家 %s 得到了Ak47", fname) //打印信息在屏幕上
fm_give_item(id, "weapon_ak47") //给予武器 Ak47
}
这样就完成啦
weapon_
后面接上的武器名 需要注意一定是小写
死亡的时候不需要让玩家得到武器,所以我们可以添加is_user_alive
再配合return
来做个判断
坐标位置又叫 坐标系
,他决定了目标在地图中的所在 位置
我们可以通过
pev_origin
来获取 目标的当前坐标
位置
示例:
new Float:origin[3]
pev(id, pev_origin, origin)
client_print(id, print_chat, "坐标位置: [X: %.0f], [Y: %.0f], [Z: %.0f]", origin[0], origin[1], origin[2])
也许你不太明白?没关系,继续看下面
坐标是由
X
,Y
,Z
三个轴组成的,其中Z轴
代表的是 上下高度。
X
和Y
轴 决定了 左右以及前后的位置
首先,我们用 pev_origin
来获取目标的坐标,然后存储到变量中(注意:因为不是字符串信息,所以不用些后面的 33
, 32
什么的)
new Float:org[3]
pev(id, pev_origin, org) //获取目标的坐标 存储到 org 数组变量中
这里这个数组变量就存储了坐标,因为坐标是 X,Y,Z
三个轴组成,所以数组中的每个值代表了 如下定义:
org[0] = X轴 (前后或左右)
org[1] = Y轴 (前后或左右
org[2] = Z轴 (上下)
- 注意:使用的
变量存储
是临时变量
如果我们要触发获取坐标的函数,代码应该是这样
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_clcmd("say /getorigin", "get_origin")
}
public get_origin(id)
{
new Float:origin[3]
pev(id, pev_origin, origin)
client_print(id, print_chat, "我的坐标位置: [X: %.0f], [Y: %.0f], [Z: %.0f]", origin[0], origin[1], origin[2])
}
我们可以利用伤害标记 pev_takedamage
来让自己免去受到伤害。通俗的说 我们可以让自己无敌
这个 pev_takedamage
有三个返回值
0.0 = DAMAGE_NO //无法受到伤害
1.0 = DAMAGE_YES //可以受到伤害
2.0 = DAMAGE_AIM //可以受到伤害(至于区别嘛 呃… 不太清楚)。一般只用到上面两个
那我们应该如何写才能无敌?
很简单,像这样
set_pev(id, pev_takedamage, 0.0) //让索引(index) 无法受到伤害无敌
是不是很简单呢?
配合主代码 我们结合起来可以是这样:
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_clcmd("say /god", "god_func")
}
public god_func(id)
{
client_pritn(id, print_chat, "我无敌啦")
set_pev(id, pev_takedamage, 0.0)
}
注意:
由于Cs自带的判断问题,你无法在下一局依然无敌。若要解决需要用到玩家思考
。但是这里暂时不讲。
点到为止。以后我们会学到用开关来写无敌
我们也许在某些服务器见过这样的效果:
[玩家名] 连接服务器。
这是怎么实现的呢?
这里,我们会用到一个叫非注册性的函数
我们知道 注册客户端命令 需要用到 register_clcmd
,该代码必须注册后 游戏中才会有应有的效果,但某些代码是不需要注册的,也就是在事件引发的时候自动触发。比如玩家连接游戏
就会自动触发一个事件,我们通过那个事件 就可以完成我们想要的效果。
玩家进入服务器触发 client_connect
函数,他不需要我们注册
示例:
#include <amxmodx>
public client_connect(id)
{
}
也许你发现了有点太简单了吧
, 是的… 通过这种函数我们甚至不用写 plugin_init
直接就可以写出函数名来触发效果
其中
id
代表index索引
即:谁连接了服务器
那我们怎么才能写出提示所有玩家某个人进入的效果呢?
这个可以配合我们之前学到的 获取名字
来实现~
示例:
public client_connect(id)
{
new fname[33]
pev(id, pev_netname, fname, 32)
client_print(0, print_chat, "玩家 %s 连接服务器。", fname)
}
你可能注意到了什么不对劲的地方,client_print
的索引怎么写的是 0
? 不是应该写id吗,嗯 看来你注意到了细节
id代表索引,我们现在也许都知道了,可是如果要发送给所有人的话,写id显然不是我们想要的效果,这就意味着 玩家进入的时候 提示的信息只发给他自己,其他人完全不知道,所以这里写
0
代表 给所有人发送信息
注意:因为是 pev
所以我们要加上 fakemeta
模块定义
所以整个代码是这样:
#include <amxmodx>
#include <fakemeta>
public client_connect(id) //连接服务器触发
{
new fname[33]
pev(id, pev_netname, fname, 32)
client_print(0, print_chat, "玩家 %s 连接服务器。", fname)
}
注意:若在服务器使用
client_connect
触发 ,会导致玩家连接时重复触发两次,若配合client_print
会显示这样的结果:
[玩家名] 连接服务器。
[玩家名] 连接服务器。
可通过添加变量来解决,此处不详讲。
和 七、判断玩家连接服务器
的方法一样,我们这里只需要将函数名改为 client_disconnect
即可
示例:
#include <amxmodx>
#include <fakemeta>
public client_disconnect(id) //断开连接时触发
{
new fname[33]
pev(id, pev_netname, fname, 32)
client_print(0, print_chat, "玩家 %s 退出服务器。", fname)
}
和 七、判断玩家连接服务器
的方法一样,我们这里只需要将函数名改为 client_putinserver
即可
示例:
#include <amxmodx>
#include <fakemeta>
public client_putinserver(id) //进入服务器的时候触发
{
new fname[33]
pev(id, pev_netname, fname, 32)
client_print(0, print_chat, "玩家 %s 进入服务器。", fname)
}
client_connect 和 client_putinserver 的区别是什么?
其实很简单,前者是连接服务器的瞬间就会触发,而后者是必须完全进入服务器之后才会触发,
client_putinserver
的触发时间段可以理解为玩家进入服务器后选择人物时/或看见MOTD时