@sogouwap
2017-06-06T04:32:25.000000Z
字数 6337
阅读 4922
前言
本教程由小灰编写,这是对于
难度:进阶
更上一层的教程
建议在对难度:进阶
学习之后再阅读
从现在,将会开始逐渐增加教程内容以及难度,请做好心理准备
我们的QQ群:139659650
点击上述列表中
未打钩
的链接,以切换学习等级。
这个教程学习下来需要多少时间?
你需要花一些时间直到你掌握它,并且每天都要坚持编写代码。
根据理解力的不同,需要的时间也不一样(三个月,六个月,或一个星期)
我需要准备什么样的系统?
你需要 Windows 7/8/10 或者 Windows XP 系统。
我们可以通过 pev
来修改玩家生命值
示例:
set_pev(id, pev_health, 120.0)
我们可以看出来,上面这个代码可以将目标的血量改成指定的值
仅仅是这样还不够,我们大部分需要的效果是 加血
(在当前生命值的情况下添加指定值
)
思路:
首先我们需要获取玩家生命值,然后再通过获取到的值来进行添加生命值
获取生命值:
new Float:hp
pev(id, pev_health, hp) //获取生命值后存储到 hp 浮点变量中
上例中存储到 hp 变量之后,那么此时要做的就是设定生命值 = ( 当前生命值 + 添加的值 )
即:
new Float:hp
pev(id, pev_health, hp)
set_pev(id, pev_health, hp + 40.0) //依靠当前生命值添加 40生命值
注意:
1. 索引(index)必须是有效的,不能是非玩家实体并且还没用(比如地图本身)
2. 设定的生命值 或者添加值
必须是浮点数。不然你会发现本来设定140的生命值变成49239239932
巨大数值了
3. 除了hp + 40.0
你还可以hp - 40.0
,发挥你的想象力吧(不过除法就算了,因为大概会变成0血)
虽然说是死亡后重生,但是实际上我们还可以做到 即使活着也能重新复活
当然这样就不具有实际价值,只是少加一个 判断
而已,我们还是做我们需要的目标吧
我们需要用到 hamsanwich
模块中的 ExecuteHamB
来触发Ham函数
Ham事件不止可以用来勾住,也可以直接用来触发(触发的时候也可以被勾住)。
比如爆炸的时候造成的伤害
我们就可以触发出来,这样即使原有不存在的效果,我们也可以自己做出来
对于Ham事件来说,重新复活的事件代码是 Ham_CS_RoundRespawn
我们通过 ExecuteHamB
来触发这个事件
示例:
ExecuteHamB(Ham_CS_RoundRespawn, id) //通过触发Ham事件中的 Ham_CS_RoundRespawn 来让 id 复活
很简单,我们需要先勾住玩家的死亡事件,然后触发函数,通过这个被触发的函数来延迟复活玩家
不太理解吗?
示例:
#include <amxmodx>
#include <hamsandwich>
public plugin_init()
{
RegisterHam(Ham_Killed, "player", "PlayerKilled", 1)
}
public PlayerKilled(vic, attacker)
{
if(!is_user_connnected(vic)) return //如果被击杀的玩家不是服务器中的 返回
set_task(3.0, "Task_Spawn", vic) //设定延迟事件(延迟3.0秒,触发 Task_Spawn 函数,被触发的索引是 vic(即:受伤者) )
}
//被触发的复活函数
public Task_Spawn(id)
{
if(is_user_alive(id)) return //如果在函数触发的时候 玩家已经复活 返回
ExecuteHamB(Ham_CS_RoundRespawn, id)
client_print(id, print_chat, "你已经复活。")
}
注意:
1. 写代码的时候需要考虑全面,比如玩家在复活函数被延迟触发的时候,此时玩家已经处于存活状态,此时就不应该继续执行复活代码,所以我们应该添加一个返回
。不然玩家会在延迟时间达到的时候重新又复活一次,这样的效果是我们不希望见到的。
2. 延迟函数的时候,触发的函数名一定要和被触发的函数名一致,并且要注意是否大小写一致(不一致的大小写会出现无法触发函数的问题)
3. 这个代码只用到了hamsandwich
和amxmodx
模块中的代码,所以这里不需要写上fakemeta
在游戏中,玩家的每次移动或跳跃都是会改变 pev_velocity
移动向量(速率)的
其移动的向量也是一个数组 和 坐标系
几乎一样,也是三个轴 X, Y ,Z
其中 Z轴
表示了目标上升还是在下降
获取示例:
new Float:vel[3]
pev(id, pev_velocity, vel)
注意是需要新建的是 浮点数的数组变量
然后通过 pev
来获取并存储到 新建变量
中
其中 vel[2]
代表 Z轴,其前面 vel[0] (X轴)
和 vel[1] (Y轴)
代表了 左右 前后
不同的移动方向
比如让目标向上飞起:
new Float:vel[3]
vel[2] = 600.0
set_pev(ent, pev_velocity, vel)
注意:
1. 新建变量用来存储的时候,必须是浮点变量
2. 如果vel[2] (Z轴)
中的浮点数值是正数那么代表正在上升或者原地不动
,反之如果是负数 代表正在坠落或下降中
本次要讲的是修改手雷爆炸时间。似乎这个方法对于 烟雾弹
没有效果,具体我没有去测试
代码 pev_dmgtime
用于获取或改变游戏中手雷的爆炸时间
可是应该怎么修改呢?
这里我们要用到一个函数 FM_SetModel
,这个函数会在设定模型的瞬间触发(本例中代表 投掷手雷的瞬间
就会执行)
示例:
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_forward(FM_SetModel, "Fw_SetModel")
}
public Fw_SetModel(ent, model[])
{
}
上例中,通过触发 FM_SetModel
事件勾住了我们的 Fw_SetModel
函数,
该函数中有两个参数,分别是:
实体
模型路径
游戏中会见到各种形形色色的实体,比如 爆炸点的箱子
,安装之后在 地上的C4炸弹
,丢弃在地上的武器
,投掷出去的手雷
等等,这些都是实体,实体还分为点实体
和固体实体
这里暂不细说。
刚刚我们稍稍了解了一下 什么是实体
,那么回到本例 即投掷出去的手雷(在空中飞的)就是一个实体
那么回到代码中: public Fw_SetModel(ent, model[])
也就是说其中那个 ent
代表了飞行中的手雷
那么我们需要在这个函数中判断什么?首先需要添加
if(!pev_valid(ent)) return
//无效实体即返回
示例:
public Fw_SetModel(ent, model[])
{
if(!pev_valid(ent)) return //无效实体即返回
}
无效的实体即 实体不存在了
、实体被删除了
、实体获取不到
pev_valid( )
在我们后期制作插件的时候尤其重要,因为无效的实体会导致游戏崩溃
或者卡住
,甚至控制台报错
为了避免这些不必要的情况,为了代码的稳定性是必须添加这个的
那么接下来我们需要做的也不会太难,我们需要再添加一个判断,判断这个实体的 实体名
是不是 手雷
实体名 (pev_classname) 是用来区分不同实体的最佳途径,对于CS来说几乎每个武器都有自己的实体名,手雷的实体名是 grenade
,玩家自己的实体名是 player
,爆炸点的箱子实体名是 func_breakable
等等。通过判断实体名
我们可以清晰的分辨哪些 需要修改
,哪些是不必要
的。
既然我们知道手雷的实体名是 grenade
接下来要做的也就很简单了:判断实体名是不是我们需要的那个
这里我们会用到一个 equal
字符串判断代码,稍后会讲到
首先获取实体名,注意这里的 索引要改成 ent 了,因为我们获取的是别的东西
new class[33]
pev(ent, pev_classname, class, 32)
现在实体名已经存储到了 class
变量中,接下来我们要做的就是判断这个 class
中是否是 grenade
字符串。简单说就是 判断这个实体的名字是不是 grenade
示例(判断是否是那个字符串):
if(!equal(class, "grenade")) return //如果class变量中 不是 grenade 字符串 那么 返回
然后我们还需要判断一下模型路径,因为 grenade
实体名 还可能包含 C4炸弹 (安装后)
或 烟雾弹
和上面的写法一样 只是要改变一下 变量名
if(!equal(model, "models/w_hegrenade.mdl")) return //如果字符串中的模型路径不是获取的那个 返回
我们可能需要同时使两个手雷类型 (高爆手雷,闪光弹) 的时间都发生变化,此时我们要用到 && 或 || 来添加多重判断
示例:
if(!equal(model, "models/w_hegrenade.mdl") && !equal(model, "models/w_flashbang.mdl")) return
不太懂?那就用代码翻译一下 即:如果不是手雷的路径 并且也不是 烟雾弹的路径 那么返回
,这样就可以避免别的相同实体名使用这个函数了
我们组合一下代码就会变成下面这样:
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_forward(FM_SetModel, "Fw_SetModel")
}
public Fw_SetModel(ent, model[])
{
if(!pev_valid(ent)) return //无效实体即返回
new class[33]
pev(ent, pev_classname, class, 32) //获取实体的名字存储到 class变量中
if(!equal(class, "grenade")) return //如果获取到的实体名不是 grenade 字符串 那么返回
//如果获取到的模型路径既不是手雷也不是闪光 返回
if(!equal(model, "models/w_hegrenade.mdl") && !equal(model, "models/w_flashbang.mdl")) return
}
写到这里,几乎就快完成目的了。接下来要做的就是修改手雷的爆炸时间,这里的手雷
指的就是实体,所以我们写上 ent
示例:
set_pev(ent, pev_dmgtime, 爆炸时间)
上面这段看上去没什么不对,但是实际上如果在游戏中测试的话,手雷扔出去后会 瞬间爆炸
所以我们还需要通过游戏时间来决定,配合 get_gametime()
完成最终效果吧!
set_pev(ent, pev_dmgtime, get_gametime() + 12.0) //爆炸时间设定为 当前游戏时间 + 12秒
我们将所有代码组合一下,就会变成这样
#include <amxmodx>
#include <fakemeta>
public plugin_init()
{
register_forward(FM_SetModel, "Fw_SetModel")
}
public Fw_SetModel(ent, model[])
{
if(!pev_valid(ent)) return //无效实体即返回
new class[33]
pev(ent, pev_classname, class, 32) //获取实体的名字存储到 class变量中
if(!equal(class, "grenade")) return //如果获取到的实体名不是 grenade 字符串 那么返回
//如果获取到的模型路径既不是手雷也不是闪光 返回
if(!equal(model, "models/w_hegrenade.mdl") && !equal(model, "models/w_flashbang.mdl")) return
set_pev(ent, pev_dmgtime, get_gametime() + 12.0) //爆炸时间设定为 当前游戏时间 + 12秒
}
如此,你就成功了
注意:
1. 本次我们初次涉及到了实体,以后我们还会学习建立一个新的实体
2. 如果你需要修改模型则需要将函数触发改为 post ,对于当前代码来说即register_forward(FM_SetModel, "Fw_SetModel", 1)
3. 若将爆炸时间改成set_pev(ent, pev_dmgtime, get_gametime())
手雷将瞬间爆炸
4.equal
的判断需要注意括号 ( )
的相符度,具体可参看我的写法
判断玩家按的什么键,这是我们在后期经常会遇到的问题
如何解决呢?使用代码:pev_button
即可
通过这个代码,可以获取到内置的一些键位,比如 基础移动(A,S,D,W) ,鼠标左键,右键,E键(使用键),R键(装子弹) 等等…
有哪些键位可以被获取到?
引用于 >> hlsdk_const.inc
(你可以查看这个文件)
IN_ATTACK //鼠标左键
IN_JUMP //跳跃键
IN_DUCK //蹲下键
IN_FORWARD //前进键(默认是W)
IN_BACK //后退键(默认是S)
IN_USE //使用键(默认是E)
IN_CANCEL //取消键(默认是ESC)
IN_LEFT //左旋键(默认是 ← 箭头)
IN_RIGHT //右旋键(默认是 → 箭头)
IN_MOVELEFT //左平移键(默认是A)
IN_MOVERIGHT //右平移键(默认是D)
IN_ATTACK2 //鼠标右键
IN_RUN //暂时不明
IN_RELOAD //弹药装填键(默认是R)
IN_ALT1 //摇杆控制键(默认是ALT)
IN_SCORE //比分查看键(默认是TAB)
那应该怎么写出判断代码?
比如说我们要判断:按下了鼠标左键
,可以这样写:
if(pev(id, pev_button) & IN_ATTACK) //如果玩家按了鼠标左键
{
}
其中
&
相当于代表一个代码链接,让其获取到的结果对定义的键位
进行对比
那如果要表达玩家 没有按这个键
呢?
我们以鼠标左键为例,需要添加一个 !
反义判断符
if( ! ( pev(id, pev_button) & IN_ATTACK) )
{
client_print(id, print_chat, "没有按下鼠标左键")
}
如果需要判断一个键位,是否处于按下但是没有持续按住的情况(比如手枪USP,按一下射一次) 应该怎么做?
这里我们还需要用到一个 pev_oldbuttons
,配合 &&
(并且) 来判断上一次键位是否依然
是那个
示例:
//如果玩家按下了鼠标左键并且没有一直按住,即继续执行函数括号中的内容
if(pev(id, pev_button) & IN_ATTACK && !(pev(id, pev_oldbuttons) & IN_ATTACK))
{
client_print(id, print_chat, '触发成功")
}