侠盗猎车:圣安地列斯CLEO入门教程
作品名:侠盗猎车:圣安地列斯CLEO入门教程
作者:璐绥居士
说明:我学习CLEO也有一段时间了。可供参考的资料少之又少,唯一的一点还都是在外国网站上找的英文资料,开着词霸我就一点一点地看啊看。最后也就能制作一些简单的作品,太复杂的还希望看到此文的高手给予指点。本教程是入门级别,主要是我学习的一些心得,希望能给对CLEO制作感兴趣的朋友们一点帮助。
引言:CLEO的环境和基本说明
《侠盗猎车:圣安地列斯》,英文名GTA:SA,是一款集第三人称射击、赛车竞速、角色扮演、恋爱养成等元素为一体的经典游戏,更可喜的是游戏给各位喜爱DIY的玩家多方面的接口,大家可以从不同角度去修改游戏使其个性化,CLEO即是基于GTA:SA的一种功能MOD,通过它可以实现很多根本不敢想的功能,比如让游戏主角在天上飞,海里的鲨鱼会吃人,像蜘蛛侠一样爬到墙上,发射六脉神剑等等,可以说是其乐无穷的一种MOD。
想在游戏中使用CLEO功能,首先需要安装CLEO环境,去CLEO的主页下载最新的cleo3,地址:http://cleo.sannybuilder.com/ 左上角有两个链接,第一个是自动安装程序。下载后安装到你的游戏目录下,这时打开游戏目录,你会发现多了一个CLEO文件夹。以后你在网上下载的CLEO就可以放在这个文件夹下了。注意只有扩展名为.cs或.cm的文件才能放在CLEO目录,.fxt文件要放在CLEO_TEXT目录下。游戏运行时加载所有cs文件,玩家可以使用全部功能。
CLEO功能主文件的扩展名是cs(CLEO Script),一些任务类CLEO还有扩展名为cm(CLEO Mission)的任务文件,放在CLEO文件夹根目录下;此外还可能有扩展名为fxt的文件,是CLEO中所需要的文本描述,可以用记事本编辑,放在CLEO\CLEO_TEXT文件夹下;如果还有扩展名为txd的,放在游戏目录下的models\txd文件夹下。若还有其余文件请参考该MOD的说明。
在游戏中使用CLEO功能,需要详细阅读帮助文件,知道其功能,使用功能的方法(快捷键或作弊码),使用功能所需要的条件(步行或乘车),以及一些可能导致游戏错误的BUG的说明,尽量避免在这些情况下使用CLEO,这是很重要的,免得你在没存档之前错误退出而捶胸顿足。
CLEO的修改和制作需要一款软件:SannyBuilder(简称SB),在其官网上可以免费下载(http://www.sannybuilder.com/ ),安装完成以后就可以使用了。如果出错需要在Tools-Options里设置SA的安装路径。
SB可以打开main.scm文件,.cs文件,.cm文件以及所有的文本文件。在打开文本文件的时候就是一个纯粹的文本编辑器;在打开CLEO文件和MAIN文件时先自动反编译出源代码存为txt放在当前目录,再打开该txt,我们做好修改以后,需要按下F7键,程序完成三个步骤:保存当前文档(txt),编译成CLEO的cs文件或main.scm文件,复制该文件到游戏目录(如果不在原始目录的话),cs和cm放到CLEO下,main.scm放到Script下,总之各归各位。大概就是这么一个流程。
这就是CLEO环境和开发环境的简单介绍,对于CLEO制作和编辑感兴趣的请继续看下文,只对使用感兴趣的就到这里为止了。
第一课:引例◆一个简单CLEO的横向剖析
CLEO的学习是从修改开始的,如果一开始就讲一大堆代码,变量,语句之类估计会吓跑一堆人,呵呵,好了不多说了,我们开始吧。
这是一个代码很简单的CLEO,曾经作为火星物品被封锁,主文件名是repair.cs,主要功能是在车上的时候,按Y键可以把车修好,就不用去喷漆处了。
以下是该CLEO源码和我作的说明,每条语句的//后:
// This file was decompiled using SASCM.INI published by Seemann (http://sannybuilder.com/files/SASCM.rar) on 13.10.2007
{$VERSION 3.1.0027}
{$CLEO .cs}
//-------------MAIN---------------
//注意以上代码不可缺少,它标志着这是一个CLEO文件,而不是main.scm文件
thread 'ENGINE' //引号里的是CLEO的名称,必须保证不会和已有的CLEO重名
ENGINE_11 //这是标签,为语句跳转所设
wait 10 //等待10毫秒
if //如果,条件判断
Player.Defined($PLAYER_CHAR) //玩家是否定义?真正含义不解,没有这个判断也行
jf @ENGINE_11 //如果条件不满足的话跳转到ENGINE_11,满足的话继续执行
if and //多条件必须同时满足,表示与的关系
0449: actor $PLAYER_ACTOR in_a_car //主角是否在车里
00E1: player 0 pressed_key 11 //是否按下了功能键11(游戏默认是Y键)
jf @ENGINE_11
03C0: $CAR_REP = actor $PLAYER_ACTOR car //定义主角所乘车辆的句柄变量
0A30: repair_car $CAR_REP //修车
jump @ENGINE_11 //无条件跳转
0A93: end_custom_thread //结束标记
大家可以在SB里新建一文档,将上面的代码拷进去,就能编译了,//是注释标记,不受影响。
看懂了之后我来做几点说明。SB的程序设计语言似乎没有循环这么一说,全是goto语句,这对适应了结构化程序设计的人来说是相当不习惯的。总是被代码拖着,一会儿跳到这里,一会儿跳到那里。但是对于游戏来说,的确很需要这种跳转,这样使得代码更容易被游戏所理解。
进入游戏后,首先判断玩家是否加载正常,不是的话重新执行,直到加载正常,然后判断玩家是否在车里并按了Y键,如不是重新执行,直到满足条件为止,设置车的句柄变量,然后用该变量就能控制玩家所乘坐的车了。本代码的目的是修车,就只有一句代码,所以说0A30: repair_car $CAR_REP才是这段代码中最重要的一句。
我们可以在这个CLEO的基础上,增加自己想有的功能,比如说觉得无偿修车太没意思了,可以在修车的同时减少玩家的金钱数:
Player.Money($PLAYER_CHAR) += -10000 //本句加在修车句之后
呵呵,这就可以了,加太多语句就跑题了。好了,本次课就到这里了,如果觉得还行的话欢迎下次再来哦,下一次就开始讲解SB的语法了,毕竟搞编程不懂语法不行啊 ^_^
第二课:opcode规则和一般语法
在上一个例子中不知大家有没有注意到一些代码前面有类似0A93:的字样,这就是所谓的opcode,每一个opcode都有一个特定的含义,目前官方似乎不愿意公布所有opcode的详细用法,这就给我们制作CLEO的制造了麻烦,我们不得不通过分析一些CLEO来研究每一条opcode的用法。一般情况下,如果你知道了一种功能的opcode,你就可以在CLEO中实现这种功能,比如修车的0A30:,只有知道这条opcode的人才会用它来编出有修车功能的CLEO。
Opcode并不一定每条语句都得有,一些简单的语句可以省略opcode,如果你编译的时候出现了错误,可能是你把不该省略的opcode给省略了,所以建议大家除了那些有例子可以省略的语句外,一律加上其opcode。
在SB界面下,你把光标定位到某一条语句时,状态栏就会显示该语句的opcode用法,比如03C0: $CAR_REP = actor $PLAYER_ACTOR car,显示的是03C0: expecting 2 params,意思是这个opcode有两个变量,表现出来就是$CAR_REP和$PLAYER_ACTOR。
好了,下面就是SB的语法介绍了。
SB语句不区分大小写。
变量:SB的变量命名规则和别的语言大不相同,大致分为两类,功能是一样的,具体喜好就看程序员自己了。它们是以$开头的字符串和以@结束的数字。举例:$sanny,10@
变量支持隐式定义,即直接使用,但全局变量需要定义。
变量的类型包括常用的整型,浮点型以外,还有句柄型,即上例中标志玩家所乘车辆的变量。
语法:算术运算和比较运算同C语言的语法,赋值语句支持+=等方法。
基本语法是条件判断和跳转。其他语句还真不怎么熟悉,用的很少。
跳转:jump @MAIN_4,提前保证有:MAIN_4的标签,以便于跳转时找到目标
单一条件判断并跳转:
If
语句,返回值必须为真或假,从英文字面上看是肯定语气的opcode
jump_if_false @MAIN_4 如果条件不成立,则跳转到指定语句
本句可简写为 jf @MAIN_4,意思一样
多条件判断并跳转:
If and
语句1
语句2
…… 必须同时满足所有语句才行
jf @MAIN_4
If or
语句1
语句2
…… 只需满足其中一条语句就行
jf @MAIN_4
可以说SB的基本语法也就这么多,没什么难的。主要的难点在于灵活运用每一条opcode,每一条opcode都有它的参数表、固定格式和功能用法,是不能乱用的,比如01B6: set_weather 1是设置天气的opcode,如果断章取义写成了01B6: repair_car $CAR,那就是严重的错误了,所以务必要弄清楚opcode的格式规范。在SB界面下按Ctrl+Alt+2可以打开一个opcode查询器,能查到所有opcode的基本用法,但也只能到这个程度,更复杂的使用和技巧就只有靠我们自己去摸索和发现了。
好了,这一课就到这里了,以后的教程我会结合各种各样不同类型的CLEO代码进行分析,让大家在不同的功能中丰富和探索CLEO的知识。
第三课:CLEO与玩家的接口
当编好了一个很经典的CLEO时,必须设置与玩家的接口,即触发条件。没有接口,CLEO就像是程序设计中未调用的函数,失去了它本身的意义。
CLEO与玩家的接口表现在三个方面:1、自动触发;2、玩家就像输入作弊码那样触发;3、玩家在键盘上按下某个键时触发。自动触发的CLEO很多,比如汽车耐久度计量槽,汽车加油,NPC跳楼等等。然而我们用的最多的,还是那些经典的,可以触发的功能CLEO。
大家从网上下载的CLEO,很多都存在快捷键冲突的问题,像上例修车那样,用功能键Y定义,实在是太不合理了。还有那个呼叫出租的,居然设成了1和2。所以我们这一课的主要目的,就是把这些不合理的快捷键改掉,让每个CLEO互相都不起冲突。
CLEO的快捷键分为两类,按键,功能键和作弊码,下面分别说明
①按键类,即按下1等单键,或Ctrl+W等组合键实现接口功能
核心代码:0AB0: key_pressed 0x73
0AB0是opcode,不可缺少,0x73是一个变量,类型为KeyCode码,比如我设置快捷键为U,代码应该写成:0AB0: key_pressed 85。字母和数字的KeyCode码同ASCII码,其他特殊字符的KeyCode列举如下:
小键盘上的键
控制键
功能键
按键
键码
按键
键码
按键
键码
按键
键码
0
96
BackSpace
8
→
39
F1
112
1
97
Tab
9
↓
40
F2
113
2
98
Clear
12
Insert
45
F3
114
3
99
Enter
13
Delete
46
F4
115
4
100
Shift
16
NumLock
144
F5
116
5
101
Control
17
186
F6
117
6
102
Alt
18
=+
187
F7
118
7
103
CapsLock
20
,<
188
F8
119
8
104
Esc
27
-_
189
F9
120
9
105
Spacebar
32
.>
190
F10
121
106
PageUp
33
/?
191
F11
122
+
107
PageDown
34
`~
192
F12
123
Enter
108
End
35
[{
219
-
109
Home
36
\|
220
Win
91
.
110
←
37
]}
221
Pause
19
/
111
↑
38
'"
222
如果想实现Ctrl+T,if语句的完整代码如下:
if and
0AB0: key_pressed 17 //Ctrl的KeyCode是17
0AB0: key_pressed 84
jf @MAIN_1
这样,就能轻易实现按键类快捷键的设置了。大家可以依葫芦画瓢,将那些不合理的或冲突的快捷键统统改掉,这下所有CLEO都能共用啦!
②功能键类
核心代码:00E1: player 0 pressed_key 19
之所以叫功能键,就是游戏中按下会起到一定效果的键,比如上下左右,开火或蹲下等,在游戏的设置中可以调,但无论调到那个键,CLEO中设置的功能键都会随之改变,没有按键的单一固定特性。设置此类功能键时应特别注意冲突问题,最好加上and和按键类一起使用。
修车CLEO的触发键是Y,这就是设置的功能键。如果你想设置成Ctrl+蹲下的快捷键,可以用if and来实现,代码为
if and
0AB0: key_pressed 17
00E1: player 0 pressed_key 20
jf @MAIN_1
以下列举常用的功能键及其代码:
2
left/right
3
steer back/up
4
special ctrl left/right
5
special ctrl up/down
6
secondary fire
7
look left
8
hand brake
9
look right
10
next radio station
11
previous radio station
12
no
13
yes
15
camera
16
brake/reverse
17
enter/exit
18
accelerate
19
fire
20
horn(in car)
21
submission
22
walk(on foot)
23
RMB vehicle mouse look
可能有一些不准,具体的大家可以自己进行测试。
功能键的设置适合制作一些武器类的CLEO,但大多数情况下并不推荐使用。
③作弊码类
作弊码的设置比较难,需要内存编辑的基础,初学者完全可以略过,以下也是我的一些心得,仅供参考。
1、eject.cs的作弊码(4字符作弊码)
核心代码:
0A8D: 0@ = read_memory 9867536 size 4 virtual_protect 0
if
04A4: 0@ == 1162495316 // @ == any
jf @EJECT_11
分析:从969110/16开始(开区间)读取4个单位长度(只能是4个),这是用户连续输入4个键的储存位置
1162495316/10=454A4554/16,拆开来就是45、4A、45、54,再转换成10进制并查出对应的ASCII字符即为密码“EJET”
操作:要修改这个CLEO的作弊码,可以按照我的步骤进行。假如修改成“ABCD”(只能是4个字符),先查出每个字符的ASCII码并分别转换成16进制,得到41、42、43、44,连起来,为41424344,转换为10进制,得到1094861636,这就是你在内存中想要读到的值。然后就可以写代码了,将上面代码的判断语句改成04A4: 0@ == 1094861636即可。
2、firework.cs的作弊码(4x字符作弊码)
核心代码:
0@ = -229907
if
&0(0@,1i) == 1179210309
jf @FW_11
0@ = -229908
if
&0(0@,1i) == 1464816203
jf @FW_11
&0(0@,1i) = 1464816128
分析:-229907应该也是内存地址,一个地址储存用户输入的4个连续键。读入用户第一次输入4个键,1179210309/10=46495245/16,拆开来就是46、49、52、45,即为“FIRE”
然后读入下一个内存地址的数据,1464816203/10=574F524B/16,拆开来就是57、4F、52、4B,即为“WORK”,连起来就是密码“FIREWORK”,最后的一句是内存的改写,避免程序重读错误。
操作:除了格式,基本原理和EJET一样,大家先把作弊码在内存的对应值算好,再带入“&0(0@,1i) ==*”比较语句即可,如果是12字符作弊码,在加一个判断语句即可,注意0@的变化规律。大家只需照套路走就是了,&0(0@,1i)的用法我还不是很懂。
3、amrifle.cs的作弊码(4字符以下作弊码)
核心代码:
0A8D: 0@ = read_memory 9867535 size 4 virtual_protect 0
if
0@ == 1095586304
jf @AMR_11
0A8C: write_memory 9867536 size 4 value 1095586304 virtual_protect 0
分析:和4字符类似,1095586304/10=414D5200/16,即为“AMR”,但是注意读取的开始内存地址,4字符是9867536,3字符是9867535。最后一句也是内存改写以避免程序重读错误。
4、laevateinn.cs(任意字符作弊码)
核心代码:
30@ = -229906
008B: 30@ = &0(30@,1i) // (int)
0085: 31@ = 30@ // (int)
31@ /= 65536
31@ *= 65536
0062: 30@ -= 31@ // (int)
if
30@ == 19521 //LE
jf @LVTN_11
30@ = -229907
if
&0(30@,1i) == 1163280724
jf @LVTN_11
30@ = -229908
if
&0(30@,1i) == 1162432078
jf @LVTN_11
&0(30@,1i) = 1162432000
分析:这段代码比较长,目的是在内存中获取“LAEVATEINN”,10个字符作弊码,可以切割来看,从右到左4位一组,LA,EVAT,EINN,算出来就是19521,1163280724,1162432078。后八位和2例类似,只是前两位的代码比较难懂。大家可以注意观察下内存的取得地址和前例的不同之处,以便触类旁通
5、hud.cs(4例的三字符补充)
核心代码:
2@ = -229908
008B: 0@ = &0(2@,1i) // (int)
0085: 1@ = 0@ // (int)
0@ /= 16777216
0@ *= 16777216
0062: 1@ -= 0@ // (int)
if
1@ == 4740420 //485544
jf @HUD_28
&0(2@,1i) = 1213547520
分析:作为4例的补充,本代码的目的是“HUD”,三字符的算法和两字符略有不同,主要是在0@ /= 16777216上面,大家可以试试把这段字符转换成16进制,即为1000000,而65536则是10000,多了两个16进制字符位,正好就能区分4位和3位密码了。大家可以触类旁通,试试2位的该怎么写。
6、PEDSpawn.cs(傻瓜的作弊码设置,适合所有人)
核心代码:
if
0AB0: key_pressed 80
jf @PEDS_113
14@ = 1
33@ = 0
PEDS_113
if and
14@ == 1
0AB0: key_pressed 69
jf @PEDS_149
14@ = 2
33@ = 0
PEDS_149
if and
14@ == 2
0AB0: key_pressed 68
jf @PEDS_185
14@ = 0
jump @PEDS_218
分析:我想不用我多说了吧,值得注意的是单纯地照着这个思路下去会有一点问题,三个字符的触发判断必须要联系在一起,本例中33@和14@变量就是联系在一起的纽带。大家注意观察观察就不难发现,如果没有这条纽带的联系,就无法实现作弊码的效果了。
这个例子代码虽然含金量不高,但通俗易懂适合所有人使用,大家只需以此类推就能写出任意个字符的作弊码,但用太多判断语句,执行效率不高,多了的话会有让人无法忍受的延迟。
这一课说了这么多,总而言之就是如何设置快捷键来触发CLEO,想当初我就是为了这个目的来学习CLEO的,呵呵,当时没有人会的东西被我研究出来了,我相当有成就感。遗憾的是并不是每一条代码都理解的十分透彻,可恶的SB并不给出详尽的解释,我想研究CLEO的路应该还很长,很长……