语法 (TeaScript)

来自SMBX 中文百科
TeaScript/Syntax
跳转到导航 跳转到搜索

该篇章主要是讲解有关 TeaScript 的语法相关的内容。

TeaScript 的语法写法逻辑与 VB.Net (或者 Visual Basic 6.0)类似,但区别非常大。所以许多在该编程语言上的习惯写法在 TeaScript 可能无法达到预料上的效果。

数据类型和变量

用户变量

用户变量(User Variable),顾名思义就是由用户创建的变量。

创建方法是在关卡&世界编辑器里按Ctrl+I,即可弹出“Variables”页面,然后点击“New”,即可创建变量,此操作可同时创建一个双精度型变量和字符串变量。

变量的命名规范如下:

  • 不区分大小写
  • 不能包含空格和其他特殊符号
  • 必须由字母或数字构成,必须以字母开头,不能为纯数字
'合法的变量名
val(a1)
val(HelloWorld)
val(eAzHeNgEy)
'非法的变量名
val(a$1)
val(123456789)
val(1Sarah)
本地变量&全局变量

本地变量(Local Variable)是指在关卡内创建好的以Val(NewVariable1)为变量的变量类型,只能在一个关卡使用,在其他关卡调用将会报错或被其他变量替换,只能从关卡编辑模式内的变量窗口创建。只作用在当前关卡,如果重新进入关卡则会重置设定值。

全局变量(Global Variable)是指在世界内创建好的以GVal(NewVariable1)为变量的变量类型,可以在当前世界的任何关卡使用,只能从世界编辑模式内的变量窗口创建。可以作用于当前地图包的任意关卡,且退出游戏也可以记录。

'本地变量(用户变量):(两种表达方式一样。)
val(NewVariable1)
v(NewVariable1)

'全局变量(用户变量):
gval(NewVariable2)
gv(NewVariable2)

在 TXTCreate 的文本里(text(id).text)或事件消息中,可以使用以下变量来为当前文本插入动态文本内容。

&val(NewVariable1)  ' 访问关卡内变量
&gvl(NewVariable1)  ' 访问全局变量

$val(NewVariable1)  ' 访问关卡内变量对应的字符串
$gvl(NewVariable1)  ' 访问全局变量对应的字符串

&sys(sysval)        ' 访问系统变量

DIM变量(Dim variable)

DIM变量是指在关卡内或世界内脚本区里往一个脚本创建Dim Variable As Type变量。该变量无需任何前缀或后缀,直接输上该变量名即可读取。

缺点是由于是在一个脚本定义,这就代表该变量只能被该脚本采用,无法跨脚本或跨本地关卡使用!(相当于当前脚本工作区的 Local 变量)
dim a as integer = 10 '定义一个 int16 整数变量 a 并赋予 10。
dim b as integer = 9  '定义一个 int16 整数变量 b 并赋予 9。
dim c as long  '定义一个 int32 整数变量 c 。
dim d as string  '定义一个字符串变量 d 。

c = a + b '把 c 赋值为 a+b 的和。
call showmsg(c)
' 显示消息:19。

d = "你好,SMBX"
call showmsg(d)
' 显示消息:你好,SMBX
DIM 变量可定义的数值类型如下列:
'======[赋值类型列表]======
dim a as byte      '允许的数值范围:-128 ~ 127(字节型)
dim b as integer   '允许的数值范围:-32768 ~ 32767(int16 整数型)
dim c as long      '允许的数值范围:-2147483648 ~ 2147483647(int32 整数型)
dim d as single    '单精度型,允许的数值范围:-3.402823E39 ~ 3.402823E38(单精度浮点型)
dim e as double    '双精度型,允许的数值范围:-1.79769313486233E+308 ~ 1.79769313486233E+308(双精度浮点型)
dim f as string    '字符串型
注意!例如上述提到的 integer 的范围是 -32768~32767,相当于 int16,同理,long (-2147483648~2147483647) 相当于 int32,没有int64对应的值。

变量类型

种类分为:双精度型变量(Double Variable),字符串变量(String Variable )

双精度变量

双精度浮点型变量是计算机使用的一种数据类型,使用 64 位(8字节) 来存储一个浮点数。 它可以表示十进制的15或16位有效数字,其可以表示的数字的绝对值范围大约是:-1.79E+308 ~ +1.79E+308。

调用输入Val(NewVariable1),可以在变量面板里修改,也可以在脚本里输入Val(NewVariable1) = xxx修改。
val(NewVariable1) = 50
字符串变量

字符串变量是基于变量的另一种类型,一般由字符和数字组成。

调用输入Str(NewVariable1)。注意,字符串没法在变量窗口直接修改!需要在脚本里用Str(NewVariable1) = "xxx"修改。
str(NewVariable1) = "Hello SMBX"

数组

数组是一些数的组合,相当于一个组里有若干个数字或字符串。

只能在关卡内变量窗口按 Shift 然后点击 "Add" 才能创建。但还不能立即使用。需要执行Call ReDim( 0 , ArrayName , Count )来定义好数组的内存个数,或者重新调整内存个数。然后就可以使用Array(ArrayName(x))来储存变量了。同样,数组下标开始位置是0。
call redim(0, arrayname, count)

' 例子
call redim(0, array1, 6)

array(array1(1)) = 1
strarray(array(1)) = "1"

' 给数组的每个下标赋值,可以进行表达式计算。
array(array1(1)) = 2
array(array1(2)) = 3
array(array1(3)) = array(array1(1)) + array(array1(2))

' 数组 array1 的数据是:[2, 3, 5]
严重警告!千万不要把变量定义在数组下标上限以外的数组位置!否则导致游戏储存不了变量导致崩溃!(下标越界一般100%会导致程序崩溃!)

控制流

控制流,简单来说,系统将整个脚本从上到下逐行执行。

例如以下这段脚本:
dim a as integer
v(variable1) = 114
a = 200
dim b as string
b = "Hello SMBX"

'脚本系统会逐步进行以下操作:
'1.添加 a 变量为 integer 类型,不赋值
'2.将本地用户变量 variable1 赋值 114
'3.将 a 变量 赋值 200
'4.添加 b 变量为 string 类型,不赋值
'5.将 b 变量 赋值 "Hello SMBX"

赋值

对变量的数据进行修改赋值,对用户变量及DIM变量及其他变量都有效。
dim a as integer = 114
v(variable1) = 209
str(variable1) = "Hello SMBX"
v(a) = 90
dim a as long = 114
dim b as string = "114"
v(a) += 50 
a += 114
b = b & "514"

' 输出:
' var(a) = 140
' a = 228
' b = "114514"

条件

将一段语句或数据进行判断分析,如果结果为真,就执行真部分的脚本,否则执行假部分的脚本。

If

If 语句的逻辑顺序是:当一个语句判断为真,执行表达式。
if condition then
    ' 执行区
end if
如果condition返回true,游戏将执行对应的语句,然后跳到end if执行其他语句。

If ... Else

在该条件流中,如果if ... then里的condition达成,则执行if ... then里的指令并结束,反之执行else指令。
if condition then
    ' 执行区
else
    ' 上述条件未满足后执行
end if

If ... ElseIf ... Else

在该多分支条件流中,如果判断达成if ... then里的Condition,则执行if ... then里的指令并结束,反之会继续逐行判断直到下一个符合Elseif ... thenCondition,如果都不符合前面的所有Condition,则执行Else指令。
if condition then
    ' 执行区
elseif
    ' 上述条件未满足后执行    
    
...

else
    ' 上述条件未满足后执行
end if
一个If中可以存在多个ElseIf,但至多有一个Else,这需要注意一下。 一些例子。
dim a as integer = 0
do
    a += 1
    if a = 64 then
        exit do
    end if
loop
dim a as string = "Mario"

if a = "Mario" then
    call showmsg("Mario")
else
    call showmsg("Luigi")
end if
with char(1)
    if .status >= 2 then
        .status = 1
    elseif .status = 1 then
        .status = 1
    else
        call spevent(2)
end with
在1.4.5里,if ... then 后面的 then 可以省略[需要验证 1]

Select Case

Select Case 是一个分支逻辑判断程序,其作用是将一个表达式的结果,按照定义好的结果对比执行结果。
select case condition
    case "condition 1"
        ' 执行 condition 1 片段
    case "condition 2"
        ' 执行 condition 2 片段
        
    ...
    
    case else
        ' 执行上述条件都未满足后的片段
end select
一个Select case内可有多个Case语句,但只能最多有一个Case Else

游戏将先计算Condition并获得其返回值,然后搜索Case之后的Expression的返回值是否与Condition的返回值一致。

如果一致,游戏将立即执行其后的语句并跳转至End Select执行其他语句。

一个例子。
dim a as integer = 60
dim b as integer = 0

select case a
    case 0 to 59
        a += 1
    case 60
        b += 1
        a -= 60
    case else
        a = 0
end select

循环

For

For 是用来定义一个重复执行的语句段。其原理是,先定义好要开始的表达式,然后按步进表达式执行,直到定义结束的表达式为止。
for counter = start to end step stepcount
    ' Execute 区域
    '你可以在里面塞一个 exit for 来结束该循环语句。
next
counter 是局部变量,start代表开始的数值,end代表结束的数值,stepcount代表一次循环之后步进的数值,它们都可以是数学表达式。exit for的数量无限制,执行完毕后,游戏将跳至next以执行for语句以外的其他语句。

如果step没有设置的话,其将被设为默认值1。

运作方式是当执行for语句时,游戏会将start的值发送到局部变量counter中,然后执行该循环体内的命令。当执行至next时,游戏会将步进数值stepcount加到counter并将counter变量的值与end比较。如果counter的值大于end,则游戏将跳至next,并执行next之后的语句,否则游戏将在for语句内重复执行语句。

一个例子。
for v(a) = 1 to 10 step 2
    call showmsg(cstr(a))
    if v(a) = 5 Then
        exit for
    end if
next

'输出:
'1
'3
'5
可以使用continue语句,此循环执行到continue时,会立即结束并继续下一次循环执行。
dim i as integer

for i = 1 to 10 
  ' If the number is even then immideatly finish the loop and continue at the next iteration
  if i % 2 = 0 then
	continue
  end if

  ' Only odd numbers will be shown
  call showMsg(i)
next

Do

Do 循环是将不断重复执行它所包含的语句,直到主动退出循环。
do
' Execute
' 你可以塞一个 exit do 用来结束这个循环。
' 如果是由事件驱动,在没有结束循环语句的情况下,请插入 call sleep(1) 阻止游戏程序卡死。
loop
While & Until
While会在执行do循环中不断判断condition,如果为真,则继续该循环。
do while condition
'Execute Scripts
loop

do 
'Execute Scripts
loop while condition
Until与while的作用一样,不同的是如果判断为真,则结束该循环。
do until condition
'Execute Scripts
loop

do 
'Execute Scripts
loop until condition
whileuntil可以放在顶部或底部的位置,且只能出现一次,如若为底部,则do程序会至少运行一次再判断。

注意!如果没有条件用来结束循环,请务必插入一个Call sleep(1)用来按游戏帧时间运行,否则会导致游戏卡死!

跳转

GoTo

强制跳转到脚本的给定标签处。
flagname:
    ' 执行区
goto flagname
flagname的命名规则与变量一致。在一个脚本中也不应有两个名称相同的flagname。不能跨函数(inter-functionally)使用goto语句,也不能在with中使用。

GoSub

强制跳转到脚本的flagname标签的行数。

当执行到该段代码时,脚本会跳转到flagname对应的行数继续执行,直到return,返回执行gosub的行继续执行。此语句不能跨函数使用,也不能在with中使用。
gosub flagname

flagname:
    ' 执行区
return
gosub FR
call showmsg("Test 1")
FR:
	call showmsg("e")
return
call showmsg("Hello")
gosub FR

' 会依次显示:"e","Test 1","e","Hello","e"。

引用对象

对于多次引用该对象的语句,可以使用with语句进行优化,这样不再需要引用该对象直接访问内部参数数值。
with object
    .param
end with
object 为游戏的一个对象,可以是npc,也可以是玩家,必定为SMBX 38A中数据库里对象中的其中一个,不能套娃使用。 例如要让NPC执行脚本:
with npc(sysval(param1))
    .x = v(x)
    .y = v(y)
end with

运算

任何脚本对数值一定的变动就需要计算,这里要用到运算符。

这里为了分清楚情况,分为了:数学表达式 和 逻辑表达式。

数学表达式

数学表达式是由数学符号和函数组成表达式的,它们经过计算后会返回一个数字。

常见的基础数学表达式如下。

符号 名称 例子
+ 加法 1 + 1 = 2
- 减法 5 - 4 = 1
* 乘法 9 * 8 = 72
/ 除法 21 / 7 = 3

5 / 4 = 1.25

Mod 模除法 (取余) 16 Mod 7 = 2
\ 无余除法 (无小数) 39 \ 18 = 2
^ 乘方 2 ^ 6 = 64
& 连接符号 (字符串常用) 2 & 3 = 23 "Hello" & "World" = "HelloWorld"
() 括号 (5 + 9) / 7 = 2
<< 左移位 32 << 1 = 64
>> 右移位 32 >> 1 = 16
常见的复杂数学表达式如下。
函数名 作用 例子
abs(x) 返回绝对值 Abs(-5) = 5
exp(x) 返回常数e的乘方 exp(5) = 148.413159102577
log(x) 返回以e为底的该数的对数 log(e) = 0.9999999999999983
sgn(x) 返回符号数 sgn(6) = 1
sgn(0) = 0
sgn(-4) = -1
int(x) 返回向下取整后的数 int(16.6) = 16
int(-16.6) = -17
int(16.4) = 16
int(-16.4) = 16
fix(x) 返回去掉小数部分后的数 fix(16.6) = 16
fix(-16.6) = -16
fix(16.4) = 16
fix(-16.4) = -16
sqr(x) 返回开平方后的数 sqr(256) = 16

sqr(2) = 1.4142135623731

sin(x) 返回正弦数 sin(pi) = -6.98296672221876e-15
cos(x) 返回余弦数 cos(pi) = -1
tan(x) 返回正切数 tan(pi/4) = 1.00000004371139
atn(x) 返回反正切数 atn(1) = 0.785398163397448

注意:三角相关函数全部使用弧度制。

特殊值

该程序里有一些常量,他们代表一个固定的数值。

常量名 描述 数值(取自游戏内的值)
Pi 圆周率的值 3.1415926535898
e 自然对数底数的值 2.718281828459
Rnd 随机数的值,取值为0~1。

随机值种子随时间的变化而变化。

0 ~ 1

取小数位15位。

注:给变量直接赋值pi,会变成 3.14159274101257,给变量直接赋值e,会变成 2.71828174591064。

逻辑表达式

逻辑表达式是用来表示参数满足的条件,在大多数语言里,大多是以布尔变量 (Boolean Variable) 表示,但在SMBX,则使用0或1代替。(不可将变量设置为布尔类型,因为不存在该类型变量。)

它们会告诉你表达式是否为真(true)。 经过运算后,结果将得到 0 (表示假,即false)或 1 (表示真,即true)。

逻辑表达式符号有两种类型:比较运算符和逻辑运算符。比较运算符的优先级要高于逻辑运算符。

同样,逻辑运算符也可以计算一个变量和另一个变量的二进制的关系。

常见的比较运算符如下:

符号 名称 作用
= 等于 如果左右相等,返回-1,否则返回0
<> 不等于 如果左右不相等,返回-1,否则返回0
> 大于 如果左操作数大于右操作数,返回-1,否则返回0
< 小于 如果左操作数小于右操作数,返回-1,否则返回0
>= 大于等于 如果左操作数大于和等于右操作数,返回-1,否则返回0
<= 小于等于 如果左操作数小于和等于右操作数,返回-1,否则返回0
TeaScript 还有一个逻辑运算符:Like(此运算符不可用,强制使用会使游戏程序崩溃!!!)

逻辑运算符只作用于数字。以下是逻辑运算符列表:

P Q P And Q P Or Q Not P Not Q P Xor Q P Eqv Q P Imp Q
0 0 0 0 -1 -1 0 -1 -1
0 -1 0 -1 -1 0 -1 0 -1
-1 0 0 -1 0 -1 -1 0 0
-1 -1 -1 -1 0 0 0 -1 -1
符号 名称 作用
not 取反值(-1 变 0, 0 变 -1)
and 如果左表达式和右表达式都为true,返回-1。
or 如果左表达式和右表达式的其中一项为true,返回-1。
xor 异或 如果左表达式和右表达式都为true或false,返回0,其中一项为true,返回-1。
eqv 逻辑相等 如果左表达式和右表达式都为true或false,返回1,其中一项为true,返回0。(相当于 not xor)
imp 蕴含 如果左表达式为true,右表达式为false,返回0,其余情况返回-1。
例子:
dim a as byte
dim b as byte
dim c as long   ' 作为输出上述运算的变量

a = 60       '0011 1100
b = 13       '0000 1101
c = a And b  '运算结果: 0000 1100 = 12
c = a Or b   '运算结果: 0011 1101 = 61
c = a Xor b  '运算结果: 0011 0001 = 49
c = Not a    '运算结果: 1100 0011 = -60
c = Not b    '运算结果: 1111 0010 = -13
c = a Eqv b  '运算结果: 1100 1110 = -49
c = a Imp b  '运算结果: 1100 1111 = -48

移位表达式

移位(<< 或 >>)是于 2021.9.13 发现的运算符。

同样,逻辑运算符也可以进行计算,具体原理可以参考:W3CSchool 演示

移位的用途,可以使用一个 Byte 类型的Dim变量来控制某样事件或决定那些事件需要触发。

运行原理是,每个数值在计算机中都是以二进制存储,比如60,在二进制的数据就是 00111100,此时,想将所有的1向左移动3位,那么只需要输入xxx = 60 << 3,就能得到运算结果为480。

在计算机中就会这样处理:

60 << 3 在计算机中的表达
12 11 10 9 - 8 7 6 5 - 4 3 2 1
原数值 60 0 0 0 0 0 0 1 1 1 1 0 0
移位1 120 0 0 0 0 0 1 1 1 1 0 0 0
移位2 240 0 0 0 0 1 1 1 1 0 0 0 0
移位3 480 0 0 0 1 1 1 1 0 0 0 0 0
举个例子:8 << 1 = 16 (在计算机中会这样处理: 8(1000) << 1(0001) = 16(00010000))

25(00011001) >> 2 = 6(00000110),其余的0会被忽略。

函数

函数是指一段可以直接被另一段程序或代码引用的程序或代码。也叫做子程序、(OOP中)方法。

一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能。所有的高级语言中都有子程序这个概念,用子程序实现模块的功能。在C语言中,子程序的作用是由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。

在 TeaScript 里,有很多种函数,它们大多数使用来控制游戏的运行或控制变量,再或者创建物体等等操作。

一些函数可以被变量赋值(只要这个函数包含返回函数 Return xxxx)。

一些游戏里定义好的函数,请去 函数 (TeaScript) 查看。

有些函数可以产生返回值,请去 返回型函数 (TeaScript) 查看。

自定义函数

TeaScript 允许你在脚本里创建一个自定义函数,但有个缺点就是没办法跨函数使用。而且一般要写在该程序的末尾处,不论在哪一行,程序都会先检测是否有函数(如果一个关卡定义了多个脚本区,则按顺序读取,如若在自定义函数前的脚本区使用自定义函数,必然报错。),然后应用,之后在任意一行调用该函数即可运行该函数里的语句。

函数分为“Procedure (过程,不含 Return 的函数)和 Function (函数,含 Return 的函数)”

返回型函数

使用下列代码,在执行函数后,产生一个返回值,可以被赋值到变量。
' 类型必须为 double 或 string。
script functionname(param1 as type, param2 as type, ..., return type)
    ' 需要调用参数,直接输入名称即可。
    return value
end script
如果该函数里没有任何 Return,或任何需要返回赋值的变量,则默认返回 0 或 ""。 使用 v(a) = functionname(param1,param2...)call functionname(param1,param2...) 都可调用。
' 例子

dim x as integer = 5
dim y as integer = 10
dim z as integer

z = max(5, 10)
' z 接收返回值 10.

script max(a as double, b as double, return double)
    if a > b then return a
    return b
end script

执行函数

使用下列代码,在执行函数后,不产生任何返回值,可以作为执行特定内容使用。
' 类型必须为 double 或 string。
script procedurename(param1 as type, param2 as type)
    ' 需要调用参数,直接输入名称即可。
end script
只能使用call procedurename(param1,param2...) 调用,如果尝试这样调用dim a as type = functionname(param1,param2...)会报错。
call functionname(...) 
dim a as type = functionname(...) '执行后会报错

script functionName(param1 as type, param2 as type, ...)
  
end script
' 例子
' 禁止跳跃或旋转跳
call setjumps(-1)

' 允许跳跃或旋转跳
call setjumps(0)

script setjumps(x as double)
  sysval(disablejump) = x
  sysval(disablespinjump) = x
end script

跨函数

使用下列代码,将允许你将该函数变为允许跨函数使用的函数,并在其他脚本调用该函数。[需要验证 1]

' This function can now be accessed in any script
export script procedurename(param1 as type, param2 as type, ...)

end script

问题

以下片段全部引用于https://wohlsoft.ru/pgewiki/TeaScript_Syntax,翻译不一定100%标准,如果出现错误请及时修正。

下面是你在使用 TeaScript 时可能会遇到的一些问题。

ScriptPtr

该函数允许修改现有脚本的参数。这个函数可能处于不可用状态,因为它总是返回一个错误。
call showmsg(hello("a"))

script hello(t as double, return double)
return 0 
scriptptr hello(t as string, return double)
return -1
end

非整数幂的弊端

sqr(x)等价于x^(0.5),因此,(-1)^(0.5)会得到一个带虚数的复数。但是 TeaScript 的数据类型并不包含该类型。
dim x as integer = -1
dim y as integer

y = (-1)^(0.5)  ' 此代码执行后会报错。
y = x^(0.5)     ' 此代码执行后不会报错,但会返回0。

文本双引号问题

由于双引号只用于定义字符串,你必须使用 chr()chrw() 在字符串中写出双引号。
str(myvar1) = chrw(34) '34 为ASCII字符中的双引号字符。(chr() 函数也可以。)

' 你可以定义一个双引号字符的变量,或者直接使用函数,两者都可以标注。
str(myvar2) = "These are some "&str(myvar1)&"special"&chrw(34)&" marks"

' 输出: These are some "special" marks.

在全局脚本上使用DIM变量

由于某些原因,在全局脚本中使用Long,Single或Double类型将在开始游戏模式时返回一个错误。

但是,Byte和Integer类型可以很好地工作。

> 这些问题都在 1.4.5 修复<

在 If 控制流里使用 Dim Variables [Patch 31 中被修复!]

如果你在If语句中初始化一个dim变量,它会产生 bug 效果。这是 TeaScript 身边的一个bug。
' 这是 bug
if -1 = -1 then
    dim i as double = 1
    call sysshowmsg(i,0,0)
    dim j as double = i - 2
    dim k as double = j + i
    call sysshowmsg(i,0,0)
    call sysshowmsg(j,0,0)
    call sysshowmsg(k,0,0)
end if

小数问题

如果在写小数的时候如果省略掉前面的0,则会出现错误。
' 错误方法
v(myvar) = .1 

' 正确方法
v(myvar) = 0.1

负数问题

写负数时,请确保前面没有其他符号(除了括号)。
' 错误方式 (将"*"和"-"放在一起导致的错误。)
v(myVar) = 5*-4
' 正确方式
v(myVar) = 5*(-4)
v(myVar) = -4*5

程序死循环

当你使用 Do 写一个循环的时候,请,务必,务必!写上sleep(1)以让该循环按每游戏帧循环运行!!!

如果不这样做的话,那么游戏会一直无限死循环运行下去该函数,这样会导致游戏卡死。

这种死循环在 For,Flags 以及任何有循环运行的控制流上也同样奏效。

下面是一些错误的例子。
Dim a As Integer
Do
    a += 1
Loop

'千万不要这样写!如果尝试在游戏中运行,会导致游戏卡死!

变量名问题

你在关卡里 Ctrl+i 打开的变量面板中创建的变量,在 TeaScript 中是val(variable)v(variable)&val(variable),在世界里 Ctrl+i 打开的变量面板中创建的变量,在 TeaScript 中是gval(variable)gv(variable)&gvl(variable)(赋予字符串时使用)。

val 与 var 有点相似,如果不注意就有可能产生错误,var 是 JavaScript 或其他语言的赋值函数。

引用

https://wohlsoft.ru/pgewiki/TeaScript_Syntax

https://unrealneo.miraheze.org/wiki/SMBX/Script/TeaScript/Control

相关群文件的 Super Mario Bros.X 脚本帮助 (功能猎手版)。

以及 Rosalina129留言) 的一些发现。

  1. 1.0 1.1 标记该引用符号的条目或内容,需要用户们帮忙进一步验证。