11 控制台变量进阶 - eLecCap1taL/CS2-CFG-Wiki GitHub Wiki

在这个章节,我们将了解控制台变量的进阶用法

管道符会是这个章节的重要前置知识,务必确保你学习了管道符章节

常见的计算用内置 cvar

cvar 的数值运算是非常重要的,但自定义 cvar 不能使用 incrementvarmultvar,我们需要一个内置的 cvar 来用于计算。它需要:

  • 不会影响游戏行为
  • 能存储数值,最好能存储小数
  • 拥有相当大的数值范围限制

幸运的是,我们找到了三个完美符合这个标准的内置 cvar,他们是:

  • joy_yaw_sensitivity
  • joy_pitch_sensitivity
  • joy_yawsensitivity

这是用于控制手柄视角灵敏度的 cvar,不会对键鼠游戏有影响,支持小数,同时数值范围相当大。我们在后面会经常使用它们。

获取 cvar 的值

我们先来梳理一下获取 cvar 值有哪些方法。🤔

方法 1

适用于所有 cvar 🤗

直接把 cvar 名当做指令,值会以 cvar名字 = 值 的形式输出。此输出可以用管道符捕获

方法 2

适用于内置 cvar,且要获取的值必须是整数或小数 🥵

运行 incrementvar cvar名字 -99999999999 99999999999 0,值会以 cvar名字 值 的形式输出。此输出可以用管道符捕获

cvar 的赋值操作

想要做计算,赋值操作是必不可少的

赋值并不简单,你可能会直接尝试 cvar1 cvar2,但这样并不能赋值,而是把 cvar1 的内容直接改成了 "cvar2"

假设我们要把 A cvar(A可以是任意 cvar,内置自定义均可)赋值给 B cvar。下面来讨论一下赋值应该如何操作

如果 B 可以存储任意类型

所有的自定义 cvar 都可以存储任意类型,同时也包括一部分内置的 cvar

如果我们在控制台直接输入 A,它的值将会以 A = 值 的格式被输出,可以用管道符捕获这种输出

我们把这个输出通过管道符传递给 toggle B,并事先把 B 赋值为 =,就完成了 cvar 的赋值

完整代码:

setinfo A 123  //A 可以是任意cvar,此处自定义一个,内置的也可以
setinfo B 0 //B 是要被赋值的 cvar,此处自定义一个。

A
//输出 A = 123

B =
//把 B 赋值为 =

A | toggle B
//通过管道符传递,此指令相当于 toggle B A = 123,又因为 B 原先的值是 =,它被赋值成了 123

如果 B 只能存储数值类型

因为所有的自定义 cvar 都可以存储任意类型,因此 B 一定是内置 cvar

这个方法稍微麻烦一些,但非常重要 😥

例如 joy_yaw_sensitivity 就只能存储数值,因此刚才的方法失效了,因为不能预赋值 joy_yaw_sensitivity= 💀👆

这里说一个技巧,incrementvarmultvar 都接受三个参数,前两个分别为 最小值 和 最大值。它们理论上也必须是数值。但事实上,如果我们提供一个文本作为参数,指令依然能执行,他会把所有文本输入当成 0 处理。这个技巧将会在后面多次用到。😉

如果 A 为正值

我们先自定义一个 cvar,名字为 -9999999999 这种很小的数字,并把 A 的值用刚才的方法赋值过去

setinfo A 123  //A 可以是任意cvar,此处自定义一个,内置的也可以

//定义临时 cvar
setinfo -9999999999 0

//把 A 赋值给 -9999999999
-9999999999 =
A | toggle -9999999999

-9999999999
//输出 -9999999999 = 123
//赋值成功

然后我们把 B 赋值为 -1,并使用 multvar,最后再乘以 -1

应该有些抽象,看代码:

-9999999999
//输出 -9999999999 = 123
//这是刚才的赋值

B -1
//把 B 赋值为 -1

-9999999999 | multvar B
//管道符传递后相当于 multvar B -9999999999 = 123
//又因为 CS2 会把非数值输入当成 0,所以最后变成 multvar B -9999999999 0 123
//可以自己想一想

B
//输出 B = -123

multvar B 0 9999999999 -1
//把 B 乘以 -1 变回正值

B
//输出B = 123

完整代码:

setinfo A 123
//B 是一个内置 cvar,只支持存储数值

setinfo -9999999999 =
A | toggle -9999999999
B -1
-9999999999 | multvar B
multvar B 0 9999999999 -1

如果 A 为负值

和刚才基本类似,可以类比理解 🤔

setinfo A -123
//B 是一个内置 cvar,只支持存储数值

setinfo -9999999999 =
A | toggle -9999999999
B 0
-9999999999 | incrementvar B

cvar 的备份

有时候,我们会需要备份一个 cvar,下面来讲解两种常用方法

方法 1

直接创建一个自定义 cvar,把要备份的 cvar 赋值过去,需要复原的时候复制回来即可 🤗

方法 2

如果要备份的 cvar 是内置 cvar,且支持数值操作。我们可以用 alias 存储。

例如,我们要备份玩家的帧数上限(fps_max),这可以由如下代码完成:

incrementvar fps_max -9999999 9999999 0 | alias recov_fpsmax 

管道符左侧的输出是 fps_max 帧率限制值

传递给右侧,被定义为了 recov_fpsmax

相当于 alias recov_fpsmax fps_max 帧率限制值

这样,在我们修改过 fps_max 之后,执行 recov_fpsmax 就可以把 fps_max 还原了 😋

这个例子的另一个重要目的是:说明 cvar 与 实际指令 之间存在关系。我们很快就会需要这种思想 🤔

cvar 取最值 / 判断 / 运算

在这一部分中,我们会默认操作的 cvar 都是可用于计算的内置 cvar。因为自定义 cvar 都可以用刚才的方法赋值给内置 cvar

同时,我们默认运算的值域不会过大,对于小数类型,不能超过 999999984306749440.000000,但示例代码中的极大极小值均不严格,都是类似 99999 这种随便敲的数字。你可以根据需求自行修改 🙂

下面的讲解部分会比较少,但是代码会有注释,可以自行多次阅读理解,或在代码中间输出 cvar 的值辅助理解 🤒

取最值

以下全部以最大值为例,如果要取最小值,乘 -1 之后取最大值即可

什么是取最值?

举个例子:123 与 50 取最大值,结果是 50

与固定值取最大值

简单的

multvar A -99999999999999999 要取的最大值 1

与 cvar 取最大值

A 1234
B 100
//假设 A 要对 B 取最大值

//值域平移
incrementvar A -999999999999999 9999999999999999 10000000000
incrementvar B -999999999999999 9999999999999999 10000000000

//取极值
incrementvar A -999999999999 9999999999999 0 | echo 1 | multvar A  

//值域平移
incrementvar A -999999999999999 9999999999999999 -10000000000
incrementvar B -999999999999999 9999999999999999 -10000000000

判断

判断 cvar 是否等于特定值

这里很有意思,我们用到了 key_findbinding

//假设我们要判断 A 是否为 114514.000000

setinfo B =
A | toggle B

toggle B fail 114514.000000 succ
//利用 toggle
//如果 B 为 114514.000000,B 会变成 succ
//否则,B 会变成 fail

B | bind JOY30
//把 B 的内容绑定给无用的手柄按键 JOY30
//例如相当于 bind JOY30 B = succ
//这显然并不是个指令,但我们并不触发它,继续往下看

alias [Player
alias 0] say 通过
key_findbinding succ | alias
[Player

最后这一段是如何检测的?

key_findbinding 文本:用于检测有哪些按键绑定了包含给定文本的内容

例如,如果你的 w 绑定了 +forward,它会输出:[Player 0] : "w" = "+forward"

我们利用这一点,搜索 succ 文本,只有当绑定为 bind JOY30 B = succ 时才会输出内容,当绑定为 bind JOY30 B = fail 时则什么也不输出

输出的第一部分是 [Player 0],这中间有个空格,我们把它传递给 alias,定义了一个名为 [Player 的 alias,内容为 [Player

因此我们只需要提前定义 [Player 为空,执行检测后,如果检测成功,[Player 的内容就是 0],否则为空。再运行 [Player,如果 0] 被运行说明检测通过

判断 cvar 是否等于另一个 cvar

与刚才类似

//假设我们要判断 A 是否等于 B

setinfo C =
A | toggle C

B | echo succ | toggle C fail
//利用 toggle
//相当于 toggle C fail B = B的值 succ

C | bind JOY30
alias [Player
alias 0] say 通过
key_findbinding succ | alias
[Player

判断 cvar 是否大于等于另一个 cvar

对另一个 cvar 取最大值,再判断相等

判断 cvar 是否大于另一个 cvar

先判断相等,再对另一个 cvar 取最大值,再判断相等

判断 cvar 是否小于(等于)另一个 cvar

与大于(等于)类似

加法

cvar + 固定数值

简单的

incrementvar A -999999999 9999999999 要加的数值

cvar + cvar

A 123
B 100
//假设我们要把 A 加到 B 上

//赋值给临时量
setinfo -2000000000 =
A | toggle -2000000000

//值域平移
incrementvar B -2000000000 2000000000 -1000000000

//以 -1000000000 为值域原点计算
//此处相当于 incrementvar B -2000000000 0 123
-2000000000 | incrementvar B

//值域平移
incrementvar B -2000000000 2000000000 1000000000

需要值域平移的核心原因是:我们需要值域全部在负数部分

这是因为 cvar名 = 在被当做 最小值 和 最大值 参数时,cvar名 是极小值(例如这个例子里的 -2000000000),= 会被当成 0,因此值域是 [-2000000000,0]

减法

和加法一样做

乘法

cvar × 固定数值

简单的

multvar A -9999999999 99999999999 要乘的数值

cvar × cvar

情况会变的有些复杂 😪

首先根据上面的说明,我们必须保证结果的值域全部在负数部分

因此我们需要先假定一个数字绝对值大小的上限 $V$。考虑到两个数字相乘会很大,不妨令其为 $100000000$,即设 $V=100000000$

我们下面的操作需要保证 $-V\le A,B\le V$

问:如果我要算的值的大小超过了 $V$ 怎么办?

答:自行调整 $V$。但你应该确保你的最终答案不会超过 999999984306749440.000000

我们要计算 $A\times B$,将其变形为 $A\times B = (A-V)(B+V)+B\times V-A\times V+V^2$

这样有什么好处?我们可以保证 $A-V\in [-2V,0]$$B+V\in [0,2V]$ ,即 $(A-V)(B+V)\in [-4V^2,0]$,这样值域就是负的了,之后再通过加减得到正确答案

下面给出一个示例代码,自定义两个 cvar,演示它们相乘的过程。(利用 joy_sensitivity)😋

之前讲过的内容将不再给出具体代码

setinfo A 1234
setinfo B 3421
//假设 V 为 100000000

    //计算 -A*100000000 并存进临时自定义 cvar mul1
    //需要的操作:赋值与 cvar * 固定值
    //此处省略具体代码

    //计算 A*100000000 并存进临时自定义 cvar mul2
    //需要的操作:赋值与 cvar * 固定值
    //此处省略具体代码

    //计算 100000000*100000000 并存进临时自定义 cvar mul3,赋值
    //需要的操作:赋值
    //此处省略具体代码

//下面是真正的 cvar * cvar

    //把 A 赋值给 joy_yaw_sensitivity,此处省略具体代码,直接赋值
    joy_yaw_sensitivity 1234

    //值域平移
    incrementvar joy_yaw_sensitivity -99999999999999999999 99999999999999999999 -100000000

    //把 B 赋值给 joy_pitch_sensitivity,此处省略具体代码,直接赋值
    joy_pitch_sensitivity 3421

    //值域平移
    incrementvar joy_pitch_sensitivity -99999999999999999999 99999999999999999999 100000000

    //创建临时 cvar 并赋值
    setinfo -99999999999999999999 =
    joy_yaw_sensitivity | toggle -99999999999999999999

    //乘法
    -99999999999999999999 | multvar joy_pitch_sensitivity

    //现在 joy_pitch_sensitivity 里存储的是 (A-V)(B+V)

//下面是一些加法

    //把 mul1 转移进 joy_yaw_sensitivity 并加给 joy_pitch_sensitivity
    //需要的操作:赋值与 cvar + cvar
    //此处省略具体代码
    
    //把 mul2 转移进 joy_yaw_sensitivity 并加给 joy_pitch_sensitivity
    //需要的操作:赋值与 cvar + cvar
    //此处省略具体代码
    
    //把 mul3 转移进 joy_yaw_sensitivity 并加给 joy_pitch_sensitivity
    //需要的操作:赋值与 cvar + cvar
    //此处省略具体代码

joy_pitch_sensitivity
//这就是最后的结果了 😀

除法

cvar ÷ 固定值

提前计算固定值的倒数,转化为 cvar * 固定值

cvar ÷ cvar

很遗憾,目前并没有直接计算的好方法。不过我们可以想想我们已经有了什么操作:🤔

  • 完全的加减乘运算
  • 判断相等
  • 判断是否大于(等于)
  • 判断是否小于(等于)

这已经完全允许我们使用 二分法 在 $O(\log_2V)$ 的时间复杂度内来确定除法的结果。

一段 C++ 代码实现,由 GPT 生成,可以用 cfg 实现相同的逻辑:

double divide(double a, double b) {
    const double epsilon = 1e-6;
    // 检查分母是否为零
    if (b == 0) {
        throw "Division by zero";
    }

    // 判断结果的符号
    bool is_negative = false;
    if (a < 0 && b > 0) {
        is_negative = true;
    } else if (a > 0 && b < 0) {
        is_negative = true;
    } else {
        is_negative = false;
    }

    // 取绝对值
    double abs_a = a;
    if (a < 0) {
        abs_a = -a;
    }

    double abs_b = b;
    if (b < 0) {
        abs_b = -b;
    }

    // 二分查找的范围
    double low = 0;
    double high;
    if (abs_a >= abs_b) {
        high = abs_a;
    } else {
        high = 1; // 当 |a| < |b| 时,商的范围是 [0, 1]
    }

    double result = 0;

    // 二分查找
    while (high - low > epsilon) {
        double mid = (low + high) / 2;
        if (mid * abs_b < abs_a) {
            low = mid; // 商太小,增加 lower bound
        } else {
            high = mid; // 商太大,减小 upper bound
        }
        result = mid;
    }

    // 根据符号调整结果
    if (is_negative) {
        result = -result;
    }

    return result;
}

😋😋😋😋

⚠️ **GitHub.com Fallback** ⚠️