CLEO/Tutorial
This is a tutorial for developing CLEO mods in Sanny Builder for GTA III and Vice City.
Contents
Installation
Install Sanny Builder into your computer. Go to http://cleo.li/ and choose the relevant link to download the latest version of CLEO.
- For GTA III:
Extract all the contents in the ZIP file, including III.CLEO.asi and the CLEO folder with everything inside, into GTA III's folder. Go to your Sanny Builder folder and open data\gta3\SCM.INI using any text editor. Go to https://raw.githubusercontent.com/cleolibrary/III.VC.CLEO/master/source/CLEO_SDK/scm.txt and copy the lines to the end of SCM.INI. Save SCM.INI.
- For Vice City:
Extract all the contents in the ZIP file, including VC.CLEO.asi and the CLEO folder with everything inside, into Vice City's folder. Go to your Sanny Builder folder and open data\vc\VCSCM.INI using any text editor. Go to https://raw.githubusercontent.com/cleolibrary/III.VC.CLEO/master/source/CLEO_SDK/scm.txt and copy the lines to the end of VCSCM.INI. Save VCSCM.INI.
Hello World!
The following outlines the basic steps on how to write a simple CLEO script and run it in the game. Run Sanny Builder and choose the relevant game on the bottom right side of the window. Select File > New... to create a new empty document. Copy and paste the following code.
{$CLEO .cs} wait 1000 ms 0ACD: show_text_highpriority "Hello World!" time 5000 0A93: terminate_this_custom_script
Save your code by selecting File > Save and save it as Hello.txt into the CLEO folder in the game's directory. Then select Run > Compile to compile your code. Hello.cs should appear in the same folder. Run the game to see your simple CLEO script display the text "Hello World!" for five seconds in the game.
Details of Hello.cs
This section explains in detail the simple Hello World! code.
- Line 1:
{$CLEO .cs}
This is a directive telling the compiler to compile a CLEO script with a .cs extension. Directives are a feature of Sanny Builder that tell the compiler to function in different ways.
- Line 2:
wait 1000 ms
This wait command stops the execution of the script for the specified time. It takes a while for the game to fade in so you might miss the upcoming Hello World! text. The wait causes the script stop for a second before proceeding onto the next command to display the text.
- Line 3:
0ACD: show_text_highpriority "Hello World!" time 5000
This shows the bulk of the complexity of scripting in the game. 0ACD is an opcode, an instruction represented by a hexadecimal number. All commands in any scripts are represented by an opcode internally. The wait in the previous line uses a keyword "wait" that uses the opcode 0001 internally. Keywords are another feature of Sanny Builder that helps simplify certain aspects of coding. show_text_highpriority
is a human-readable description of the command. This text is unimportant when the program compiles your code. time
is also unimportant when compiling and are there only for human readability. In essence, the bare minimum for getting this line to compile in the program is 0ACD: "Hello World!" 5000
. Move your text cursor to this line and on the bottom left corner of the window you can see the message "0ACD: expecting 2 params." Opcode 0ACD requires two inputs, which in the example are "Hello World!"
and 5000
. 0ACD is exclusive to CLEO and does not work without CLEO installed.
- Line 4:
0A93: terminate_this_custom_script
0A93 is another opcode that is exclusive to CLEO. This opcode ends the CLEO script. This opcode requires no inputs. Again, terminate_this_custom_script
is a human-readable description of the command, so the bare minimum to get this to compile is 0A93:
.
Flow of control
Loops
The Hello World script executes each command once. If you want to repeat the same commands, you can place your code in a continuous loop. Replace your Hello World code with the following code:
{$CLEO .cs} 0000: while true wait 0 ms 0ACD: show_text_highpriority "Hello World!" time 0 end 0A93: terminate_this_custom_script
Compile your code. Run the game to see your simple CLEO script display text indefinitely.
- Line 2:
0000:
This opcode is a no operation, meaning executing this command produce no effect. Because the first line that is not a no operation is the start of a loop, this line is needed to avoid a jump-at-zero-offset bug.
- Line 3:
while true
This is the start of the while loop. The condition of the while is always true, so it will loop indefinitely. The while loop is closed off in line 6. The while construct is a feature of Sanny Builder. When compiled, internally opcode 0002 allows the code to jump back to the beginning of the loop.
- Line 4:
wait 0 ms
Any loops that do not terminate as soon as reasonably possible require a wait, as in this case. Without a wait, the game locks itself into processing the script and cannot process the rest of the game. Note that this line and line 5 are indented. Indentations are unimportant when the program compiles your code and are there only for human readability. It is good practice to indent your code within loops so that you can see where the loop affects your code.
- Line 7:
0A93: terminate_this_custom_script
The game will never reach this line because the script loops indefinitely. You can omit this line if you choose but this is placed here in case you modified the example code to exit out of the loop.
Conditions
Sometimes you want some commands to execute only if certain conditions are met. An if-then statement is a basic way to do this. Replace your Hello World code with the following code:
{$CLEO .cs} 0000: while true wait 0 ms if 00E1: player 0 pressed_button 17 then 0ACD: show_text_highpriority "Hello World!" time 0 end end 0A93: terminate_this_custom_script
Compile your code. Run the game to see your simple CLEO script display text only if you are pressing the ATTACK key.
- Line 5:
if
This is the start of the if-then statement. All if-then statements require an "if", "then", and "end" keywords in that order. The condition is between the "if" and "then", and the commands to execute are in a block between "then" and "end".
- Line 6:
00E1: player 0 pressed_button 17
This is the condition for the if-then statement. If the condition is true, e.g. the key is being pressed, the game executes the block of commands following "then". It continues executing commands after reaching "end". If the condition is false, e.g. the key is not being pressed, the game skips over the block of commands following "then" and instead starts executing commands after "end". As noted before, indentations are there only for human readability. You can see that they clear up any confusion on which "end" keyword belongs to "if" or "while". Conditions in Sanny Builder are denoted by the extra spaces between the opcode and data; the extra spaces are also there only for human readability.
So far the if-then statement can only check for one condition. To check for more than one at a time, you have to use additional keywords.
{$CLEO .cs} 0000: while true wait 0 ms if and 00E1: player 0 pressed_button 13 00E1: player 0 pressed_button 17 then 0ACD: show_text_highpriority "Hello World!" time 0 end end 0A93: terminate_this_custom_script
Run the game to see your simple CLEO script display text only if you are pressing both the CAMERA and ATTACK keys.
- Line 5:
if and
This is the start of the if-then statement but instead of just checking for a single condition, this checks for more than one conditions. If all the conditions are true, e.g. both keys are being pressed, the code will execute the block of commands following "then". The game limits up to eight conditions under one "if". If you want to check for even more conditions, you have to nest children if-then statements inside the parent if-then statement.
Another variant of checking for multiple conditions is checking if at least one of the multiple conditions are met.
{$CLEO .cs} 0000: while true wait 0 ms if or 00E1: player 0 pressed_button 13 00E1: player 0 pressed_button 17 then 0ACD: show_text_highpriority "Hello World!" time 0 end end 0A93: terminate_this_custom_script
Run the game to see your simple CLEO script display text only if you are pressing either the CAMERA or ATTACK keys.
- Line 5:
if or
Just as before, instead of just checking for one condition, this checks for more than one conditions. But the important difference is that if at least one condition is true, e.g. either one or the other key is being pressed, the code will execute the block of commands following "then".
GXT hook
The base game stores most text into a set of files with the extension GXT. A key is associated to a string of text, allowing the script to use that key to display the full text. CLEO comes with a GXT hook that allows you to add additional keys without the need to modify the GXT files. Replace the Hello World! code with the following code.
{$CLEO .cs} wait 1000 ms 00BC: text_highpriority 'HELLO' time 5000 flag 1 0A93: terminate_this_custom_script
Go to CLEO_TEXT folder and create a new file named Hello.fxt which can be opened using any text editor. Inside that file, copy and paste the following line.
HELLO Hello World!
The FXT file is a plain-text file for CLEO and the game to read custom text from. Each line in the file consists of the key and data. The key is a unique text that helps the game find and retrieve the actual data. Its length must be seven characters or less and must not contain a space, which is used to separate the key from the data. The data contains the full text that the game can display on the screen. In this example, opcode 00BC uses the key 'HELLO' to display the Hello World! text. Note that the key is between single quotes rather than double quotes; this tells the compiler to compile this data as an 8-byte string as required by this opcode.