Quantcast

Jump to content

» «
Photo

Mission Coding for Dummies!

  • This topic is locked This topic is locked
19 replies to this topic
Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#1

Posted 21 March 2009 - 09:27 PM Edited by Dutchy3010, 15 June 2009 - 02:18 PM.

Mission Coding for Dummies!


Lots of people want to learn coding, but when they see the code, they give up. This is a big tutorial that will walk through the basic options of SCM-coding. I can promise that you are able to create a simple mission when you worked through this tutorial. The best way to learn it with this tutorial, is to try every complete script, so you will see what happens ingame. This way you will learn it faster and better, because you can visualize it in your mind.

The difference between this tutorial and most others, is that this tutorial is written from another point of view. It contains (at this moment) 12 short tutorials, which you can follow separately. In every tutorial, you will learn another aspect of coding. The first 3 tutorials aren't about things ingame, it is about coding itself and about the structures. You have to learn it first, but this won't be as pleasant as the tutorials that follow. Then you will learn things like creating actors and cars, the basics of a mission. Almost every tutorial will end with a stripped file and the code, so that you can use it in SannyBuilder to see what you did learn. You can also use this as a base for your own experiments to see what other opcodes do.

We will use SannyBuilder, because learning to code is easier with this tool.

For these tutorials you need:
  • SannyBuilder (Download)
  • A "stripped" code (SannyBuilder folder > Data > SA > Stripped)
  • Grand Theft Auto: San Andreas
  • Ped Editor (Download)
  • San Andreas Place Manager (Download)
Content: Links: Please do not post in this topic (so I can extend it later)!

Thanks to PatrickW for his help!
  • Indi, Alex5526 and cyrilaeshell like this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#2

Posted 21 March 2009 - 10:58 PM Edited by Dutchy3010, 21 March 2009 - 11:36 PM.

Introduction


Content:
  • Introduction
  • What do I need?
  • The programs
  • SCM Coding
    • Structures most important!
    • Coding
  • CLEO
    • Introduction to CLEO
  • Finally
Introduction
This tutorial is only for people who want to learn coding. When you want to learn coding, where do you have to start? You can't see the wood for the trees. In this tutorial, I will help you with these first steps. Steps which most people think are to difficult, so they quit. Everyone who puts effort in it, can learn coding! But don't think: I want to learn coding, so tomorrow I will make my first mission. This is of course not the proper way to do it. Learning to code takes time, just like when you want to learn to use other programs, like Photoshop or 3dsmax.

What do I need?
Some people wonders what is needed to code:
  • A program to edit main.scm
  • A GXT editor (SA GXT Editor)
  • San Andreas Place Manager (not for coords, but for teleport)
  • Map Editor
  • Ped Editor
  • Time/Effort
Programs

FunctionMissionBuilderSannyBuilderNotepad
Editingyesyesyes (when already decompiled)
Compileyesyesno
Decompileyesyesno
Updatesnoyesno
ExtrasLittleYesNo
CreatorBarton WaterduckSeemannMicrosoft
DownloadKlikKlik-
Extra's in SannyBuilder mean things such as: CLEO3, Built-in coords-tool, opcode search tool, lot of hot-keys, colored texts, etcetera. I advise everybody who starts learning to code, to use SannyBuilder.


SCM Coding
Structures most important!
Of course everybody wants to write code immediately, making missions or other things. I will advise against this, because when you first learn the structures of SCM-coding, it will be easier to script afterwards. You can compare this to school: first you have to learn things (which is less pleasant) so you will get a better job in the future. This is the reason why we will start with structures after the two introduction tutorials.

Coding
After learning the structures, you shouldn't try to make a total mission yet. A better start is to spawn some pickups. Then you can try to spawn a car or an actor, which we will explain in further tutorials. So you will learn inch by inch to make a small mission, which we also will do in further tutorials. So, if you want to learn to make a small mission, you have to follow all of these tutorials and you will learn it. After you made a small mission, you can extend it, bit by bit, so you will learn more advanced coding.


CLEO
CLEO is an extension to SCM scripting. It mainly adds two things: CLEO-scripts and new opcodes. I advise to start with SCM first. When you can code SCM, you can also wrote code with CLEO. The other way round it is more difficult, because of some structures within SCM, which you don't have in CLEO.
But what are the advantages of CLEO?. With a CLEO-scripts you don't have start new game, which is very handy for most of the small mods. There are also some great opcodes added. You can also use the new opcodes in your SCM mods, but users of your mod will than also need to have CLEO installed. You can find all the differences between CLEO and SCM in the SannyBuilder help files: Help > Contents > CLEO 3 Library.


Finally..
When you have questions, you can always ask it in the Coding Help Forum, and you will get an answer as fast and as good as possible! Good luck in the following tutorials!
  • Indi likes this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#3

Posted 21 March 2009 - 10:59 PM Edited by Dutchy3010, 21 March 2009 - 11:36 PM.

Before you begin


Decompile
What is it?
Before you can edit the main.scm file, you first have to decompile. This means that it will become humanly readable, you transform the extension from .scm to .txt. You can do this in programs as MisionBuilder and SannyBuilder, but not in notepad. Once you have the txt file, you can also open it in notepad or any other text editor. This isn't very handy, because there aren't any extras in notepad (which makes it a lot more difficult).

How to decompile
Because these tutorials are about SannyBuilder, I will explain it for SannyBilder.
Press F5, or the icon "Decompile" or "run" > "Decompile".

user posted image

Go to the folder: Program Files > GTA San Andreas > Data > Script and select "main" (a .scm file). Open it.

user posted image

When you are doing it for the first time, you will get a screen like below. Select the folder "GTA San Andreas". Then you have to do the previous step again.

user posted image

Now you will get the text, which you can edit. When you save it, you will get a txt file.


Compile
What is it?
This is the opposite of decompile. When you want to test your code, you have to transform it back to the language which GTA SA can read. So you have to make it a .scm again. You can do it by pressing F6, press the icon of "compile" or by "Run" > "compile".

What is the difference between compile and compile + copy?
You can choose to save your txt file is at another folder on your pc than the original Data folder. Compile means that the SCM file will be created in the folder where your .txt file is saved. Compile + Copy means that the SCM file is copied into the Data folder after compilation. So the main.scm in the data folder will be replaced. There is no difference when you are editing the main.txt that is located in the "data" folder itself.


SannyBuilder
Colours

In SannyBuilder are some words marked:

user posted image
  • Black: these are often used commands, like jump, wait, create_thread, fade, repeat, jf, etcetera. You don't have to put the code before it.
  • Green: these are labels, for example ":label" or "@label".
  • Blue: the variables have a blue colour.
  • Red: names of for example objects, texts and threads are red.
  • Yellow and blue: Yellow is the "object", the blue is what is said about the object. It is difficult to explain, but when you look at it, I'm sure you will understand it.
  • Brown: names of the models.
  • Blue/Grey (light): this are comments, they are not read by the game and are handy as a note. They are proceeded with //.
Functions
There are a few functions in SannyBuilder, which are very handy to use. Of course there are more functions, but these are the most important ones.
  • Coords manager. You can find it in Tools > Ide Tools > Coords Manager (hot key: CTRL+ALT+1). You can find the X, Y and Z coordinate of the place where you are in San Andreas. You can also find your angle. You can't use this when SA isn't running of course.
  • Opcode search. You can find it: Tools > Ide Tools > Opcode Search (hot key: CTRL+ALT+2). With this tool you can find opcodes very easily.
  • F1 key: when you typ a little part of a command, sannybuilder will try to find a command as it was used in the original main.scm.
  • SCM documentation. You can find it under the help-menu (hot key: F12). You will find a usefull database. You can find here for example the icon-numbers.
  • Start SA: you can start SA by pressing F8 or the icon "Run San Andreas".
  • Missionbuilder > Sannybuilder converter: it doesn't work perfect, but you can find it under Tools-menu > Code Converter > MB -> SB.
  • Indi likes this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#4

Posted 21 March 2009 - 11:00 PM Edited by Dutchy3010, 25 March 2009 - 10:07 AM.

Coding Structures

Threads
The main.scm file is processed by multiple threads.This might be one of the most difficult concepts to grasp for someone not familiar with multi-threading programming. So don't despair if you don't get it right away, you're not alone.

Up till now, we described the code as being executed opcode after opcode, sometimes jumping to a different location and continuing. Imagine this process being executed by a little guy inside your computer. He spends all day reading an opcode, executing it, determining the next opcode to execute, read it and execute it, day-in day-out. Lets call this little guy "thread".

Now image that this guys has a brother, who does exactly the same thing. Off course they're not looking at the same opcodes at the same time, his brother is working on another part of the code. Both are working independently of each other, reading opcodes, executing them, determining next opcode, ... Can you image that ??

Now image that there's a whole family of those little guys, all doing the same thing on different parts of the code.

~~~~~Welcome to the world of multi-threading. ~~~~

Now how do we manage these threads ?
At the beginning there's only one thread that starts to execute at the beginning of the main.scm file, called the main-thread. Through a serie of jump's it will skip the first three sections and arrive at the beginning of the code-section. There it will perform some initialisation, until it encounters a series of "create_thread" opcodes.

That opcode creates a new "little guy" (thread) that starts executing opcodes below the given label. In this way, the main-thread creates a whole army of threads, each working on a specific part of the code, to perform a dedicated task. Some of the threads live indefinitely, just looping around the same code. Other threads only have to perform a single task once and kill them self upon encountering an "end_thread" opcode.

A thread can also kill another thread, by supplying name of its victim as a parameter to the end_thread opcode. Threads are given a name, when they execute a "name_thread" opcode, which is normally one of the first opcodes they encounter.

Create_thread may be executed from both the MAIN part as well as the MISSIONS part. The code that the thread will execute however, must be located in the MAIN part.

Missions are also executed as threads, but they are started with "start mission". This will create a special kind of thread oif which only one can exist at the same time. Furthermore, during this thread the engine continually checks whether the player is busted or wasted. In that case the "little guy" is instructed to perform a "return" opcode, which causes him to jump back to the last "gosub". This is why missions should have a special structure, in which this will lead to a failed mission. This structure will be described in one of the tutorials to come.

Labels/jump/gosub
Labels are marked with a ":". These are names that are assigned to some locations in the code. It is used jump to a location in your code. You can do this with "gosub" and "jump".

CODE
:MAIN_4059
01B7: release_weather
jump @MAIN_4075

You can see a label, named "MAIN_4059". Then you can see a command, with a code (01B7) called opcode. In the third line you can see another command: "jump". With the "@..." after that you indicate where the code has to jump to. So you are jumping to ":MAIN_4075".

Gosub works different, it also jumps to another label, but it goes back to the location just after the "gosub" when he reaches a "return"-command. For example:

CODE
:MS_GAME_BEEFYBARON
$ONMISSION = 1 // integer values
00BA: text_styled 'BEEFY' 1000 ms 2  // Beefy Baron
gosub @SUB_FADE_500MS
start_mission 10  // Beefy Baron
jump @END_CASE_VIDEO_GAME


CODE
:SUB_FADE_500MS
0169: set_fade_color 0 0 0
fade 0 500

:LITCAS_282
if
fading
else_jump @LITCAS_306
wait 0
jump @LITCAS_282

:LITCAS_306
Return


So it jumps with a "gosub" to ":SUB_FADE_500MS". When it reaches the "return" command (under ":LITCAS_306"), it goes back to the gosub. After the return, the code will continue with "start_mission 10").

Gosub is used when you want to use a piece of code from multiple places in your code.

Conditional jump
When you use a "Conditional jump", the code will do or not do the jump depending on some condition. You can use three notations for this, which all have the same result:
“else_jump @boe”
“jf @boe”
“004D: jump_if_false @boe”

CODE
:SWEET4_103
if
  Model.Available(#GREENWOO)
else_jump @SWEET4_152


In this example we check if the model is available (the model of the Greenwood, in this case). When this isn't true, it jumps to label ":SWEET4_152". When this is true, the code goes further.

With the conditional jump, you can make a loop. Then the code waits until the condition is true. Be Aware! You have to put a wait in every loop! When you forget it, the game will crash, and, when you don't run SA in a window, you even can't go back to your desktop. The only solution is to restart your PC. So, my advise is to run SA in a window when testing your new code! An example of a loop is:
CODE
:SWEET4_103
Wait 0 ms
if
  Model.Available(#GREENWOO)
else_jump @SWEET4_103

As you can see, the code stays in this loop, until the model is available.

If-then-end structure
There are other structures which you can use to check conditions. For example the if-then-end structure. For example:
CODE
if $choice == 0
then
$money = 10
End

When the variable $choice equal is to 0, then the variable $money will become 10. If $choice isn't equal to 0, nothing will happen. You have to put an "end" to indicate where the code should continue when the condition is not true. You can add more things between "then" and "end" that will only be executed when the condition is true.


Repeat-wait-until structure
CODE
repeat
 wait 0 ms
until 03D0: wav 3 loaded

This is an example of a repeat-wait-until structure. This is a loop, so there has to be a wait in it. This code means: do everything between repeat and until, until the condition is true. Between repeat and until there can be also other things, that will be executed over and over again until the condition is true. Instead of using repeat-wait-until, you can also write it as a conditional jump (this will behave exactly the same!):
CODE
:label
wait 0 ms
if
03D0: wav 3 loaded
else_jump @label



Numbers
There are two kinds of numbers:
Integer values: 1 2 3
Floating-point values: 1.5345 3.4

Floating-point values will be used as coordinates, angles or things like speed.

Integer values are used for most other things. The advantage of Integer values, is that computer can easy calculate with these. These numbers are for example used for waiting periods.

The Integer Values and Floating-Point values can't be mixed up, because then the code won't work.

CODE
0004: $abc =  5000;; integer values
0084: $def = $abc;; integer values and handles


The difference is that the first a assigns a value to a global variable. In this case 5000 to global variable $abc. Then you say, that $def has to get the same value as $abc. So this code means: $def = $abc = 5000 (But you may not use this in the main.scm, there you have to split it up)


Variables
There are two kinds of variables: global and local. Global variables are indicated with a $ (for example: $abc), local are indicated with a @ at the end (for example: 2@). Local variable-names only consists of numbers, a global variable has to consist of characters and digts. When a Global Variable only consists of numbers, it will be handles differently by the compiler. This can lead to unexpected results, so you shoudl make sure that your variablenambles contain at least one character. The difference between local and global variable is that you use a global through the whole script. A local variable is applies only within one thread. When you assign a value to 1@, and you want to use that number in another thread again, it won't be avaiable, you have to assign it that value again. The value of the other thread doesn't apply any more.

Calculate with variables
Sometimes you have to calculate with variable. There are some opcodes for this. Caution: there are different opcodes for integer-values and floating-point-values, and there are different opcodes for global or local variables. You can add, subtract, multiply and divide. Some examples:

CODE
0008: $variable += 1


CODE
000A: 3@ += 3000


CODE
000B: 6@ += 0.1


CODE
0009: $variable += 1.741


CODE
0058: $variable += $variable23 // (int)


Beware not to confuse these two opcodes:
CODE
0038:   $variable == 1

With this opcode, you will check if $672 is equal to 1.

CODE
0004: $var = 0

With this opcode you will set the global variable $var to 1.
  • Indi likes this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#5

Posted 21 March 2009 - 11:02 PM

Actors


There are 2 types of actors: 'normal' actors and 'special' actors. The normal actors are easier to make, because they have to be loaded just like normal models (like weapons). The special actors are a bit more difficult, but I will explain it later. We will begin with a "stripped" code, that is a code which is clean. The only thing you can do, is walk/drive around. It is perfect for a new mod. An actor is a character in the game, like Sweet, but also random pedestrians. First we are going to spawn a normal actor. You have to do it in three steps:
- Load actor models (and check if it is loaded)
- Spawn actors
- Unload models

Load actor models (and check if it is loaded):
CODE
// Load models
0247: load_model #BFYST
038B: load_requested_models

I think this part of the code speaks for itself, but I will explain it shortly. When you typ //, it is a comment, like it was told in previous tutorials. This is only a note for the coder. In the line "load_model", you have to say which model you want to use. If you are looking for a specific ped, you can find the name by using the ped editor. I chose for BFYST, but there are also other possibilities. You have to write the third line because then the previous models will actually be loaded.

Then you have to check if the model is loaded:
CODE
:MODEL_LOAD
wait 0 ms
if
   8248:   not model #BFYST available
else_jump @MODEL_SPAWN
jump @MODEL_LOAD

:MODEL_SPAWN

You have to make a new label, because you are creating a loop. Then you know where you can jump to. It doesn't matter which name you give to the label, I called it "MODEL_LOAD". I advise you to give it a clear name, because it is easier to find it when you have a big mod.
When you followed the previous tutorials, you know what the jump-if-false (or else_jump) structure is. This is a example of its usage. If the model " BFYST" isn't available, then jump to MODEL_LOAD. If it is available, jump to :MODEL_SPAWN.

The following code is the same:
CODE
:MODEL_LOAD
wait 0 ms
if
0248:   model 15@ available
else_jump @MODEL_LOAD


Spawn Actor
CODE
:MODEL_SPAWN
10@ = Actor.Create(CIVFEMALE, #BFYST, 0.0, 0.0, 0.0)
Actor.Angle(10@) = 318.9705

The 10@ is a local variable, it doesn't matter which number you are using. It has to be under the 32, because that is the maximum within a thread (when you are using an "official" mission structure, it can be more then 33). Then we are going to assign the variable a value, in this case an actor. So we give the actor, in this case, the name "10@". Actor.Create is the command, I think it speaks for itself. After that, you will see 5 parameters. First the type of the ped, CIVFEMALE. Then the model name and after that there are 3 coords: x, y and z. Like I already told in previous tutorials, there is a built-in coords tool in SannyBuilder, you can use that to determine coordinates like these.
The Actor Angle is the direction of the ped. 0.0 is to the west. So if you want him to stand to the east, you have to give it an angle of 180.0. You can also use the coords tool for this.

Model unload
Finally you have to unload the model:

CODE
0249: release_model #BFYST

Caution! Release_model doesn't mean that he actor will disappear! Just like loading the actor doesn't mean that you spawn the actor. You have to use another opcode to delete the actor (e.g. 009B: destroy_actor 18@)

So the complete code for an actor spawn is:
CODE
// Load models
0247: load_model #BFYST
038B: load_requested_models

:MODEL_LOAD
00D6: if
8248:   not model #BFYST available
004D: jump_if_false @MODEL_SPAWN
0001: wait 0 ms
0002: jump @MODEL_LOAD

:MODEL_SPAWN
10@ = Actor.Create(CIVFEMALE, #BFYST, 0.0, 0.0, 0.0)

//Here you do what you want to do with the actor.

0249: release_model #BFYST


With the Ped Editor you can find your ped-model, you will also get a view of the ped (how he looks ingame). You will see the name of the ped and the group/type. So for making an actor spawn, you need SannyBuilder (to edit the file), coords tool (in SannyBuilder) and Ped Editor to look up the names of the actor-models.


Special Actors

Special Actors are a bit more difficult. You have 10 "slots" for special actors models. You can compare this to the 8 slots in which you can save you savegame: you can only have 8 at one time, and if you want another savegame, you have to delete one first. To see a list of all special actors, you can look in the helpfile: Help > Contents > SCM Documentation > GTA SA > Special Actors.

When you understand the normal actors, you can also understand special actors. It looks a similar, but with slightly different code. Again, you have to load the model, check if the model is available, spawn the actor and unload the model.

Loading the model
We take Catalina for example. You will see, when you look to the SCM Documentation, that she has the name 'CAT'. First we are going to load the model:
CODE
// Load models
023C: load_special_actor 'CAT' as 1 // models 290-299
038B: load_requested_models

So in this code, you load special actor Catalina (CAT), at slot 1. Next to this one, you can load upto 9 others.

Check if it is loaded:
CODE
:MODEL_LOAD
if
823D:   not special_actor 1 loaded
else_jump @MODEL_SPAWN
wait 0 ms
jump @MODEL_LOAD

:MODEL_SPAWN

This is almost the same as the "normal" actor. Only the second line is a different, the structure is the same: when the model of the special actor isn't loaded, jump to the label MODEL_LOAD. If it is loaded, jump to MODEL_SPAWN. The 1 is in this case the number of the slot, in which you loaded it.

Spawning the actor
CODE
:MODEL_SPAWN
009A: 10@ = create_actor_pedtype CIVFEMALE model #SPECIAL01 at 0.0 0.0 0.0
0173: set_actor #SPECIAL01 Z_angle_to 0.0

It is exactly the same as for 'normal' actors, except that the models are named #SPECIALxx. XX depends on which slot you used. In this case, it is 01, because it was slot 1. If it was slot 2, you have to use: #SPECIAL02.
CIVFEMALE is the pedtype, the angle is just like normal actors.

Unload the actor
Finally you have to unload it:
CODE
0296: unload_special_actor 1

So we unload the model of the special actor, which is in slot 1.

The whole code to spawn a special actor:
CODE
// Load models
023C: load_special_actor 'CAT' as 1 // models 290-299
038B: load_requested_models  

:MODEL_LOAD
00D6: if or
823D:   not special_actor 1 loaded
004D: jump_if_false @MODEL_SPAWN
0001: wait 0 ms
0002: jump @MODEL_LOAD

:MODEL_SPAWN
009A: 10@ = create_actor_pedtype CIVMALE model #SPECIAL01 at 0.0 0.0 0.0
0173: set_actor 10@ Z_angle_to 0.0

//Here you do what you want to do with the actor.

0296: unload_special_actor 1


When you include a normal actor to a stripped file!

You can let the actor do many things, like get in a car, or shoot at someone. But first you have to spawn them, then you can add other things. That is why it is essential to know this. I hope you learned something from this tutorial, and if you have any questions, just let me know!
  • Indi likes this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#6

Posted 21 March 2009 - 11:04 PM

Give weapons to actors


Introduction
In the second part of this tutorial, we will extend the code of part 1. We will give the actor a weapon, and will set some other properties. The player itself gets a weapon too.

To make it more fun, the player and the actor needs to kill each other. If you kill the actor, you will get music, a mission passed text and 25000 dollar.

We will start with giving the actor a weapon.

Before giving a weapon to an actor (the player is also an actor!), we first have to load the weapon-model. Just like we did in part 1 for the actors. It is exactly the same as loading the model for a "normal"-actor.

This was the code for loading an "normal" actor:
CODE
// Load models
0247: load_model #BFYST
038B: load_requested_models

:MODEL_LOAD
wait 0 ms
if
   8248:   not model #BFYST available
else_jump @MODEL_SPAWN
jump @MODEL_LOAD

:MODEL_SPAWN


We will extend this part of the code by loading the right models of 3 weapons: the desert-eagle, the rocket-launcher and the M4.
In the help-files of SannyBuilder we look up the model names and the numbers of the three weapons: Help > SCM Documentation > GTA SA > Weapon Numbers.

There we find the following data:
  • desert-eagle 24 #DESERT_EAGLE
  • rocket-launcher 35 #ROCKETLA
  • M4 31 #M4
We extend the code above with the model names of the weapons:
CODE
// Load models
0247: load_model #BFYST
0247: load_model #rocketla
0247: load_model #desert_eagle
0247: load_model #m4
038B: load_requested_models

:MODEL_LOAD
wait 0 ms
if or
   8248:   not model #BFYST available
   8248:   not model #rocketla available
   8248:   not model #desert_eagle available
   8248:   not model #m4 available
else_jump @MODEL_SPAWN
jump @MODEL_LOAD

:MODEL_SPAWN

Now the code will stay in the loop as long as any of the model isn't loaded yet. You have to make "if" a "if or", because their is more then one condition. "if or" means that one of the conditions has to be true, "if and" means that all of the conditions have to be true. So in this case, the code waits until all 4 models are loaded.

Now we are going to give the weapons. We will start with giving the player a rocket-launcher and a desert-eagle:
CODE
01B2: give_actor $PLAYER_ACTOR weapon 35 ammo 10 // Load the weapon model before using this
01B2: give_actor $PLAYER_ACTOR weapon 24 ammo 30000 // Load the weapon model before using this
01B9: set_actor $PLAYER_ACTOR armed_weapon_to 24

Here you will need the numbers of the weapons which we found in the help-files. We will give the player a rocketlauncher (number 35) with 10 rockets and a desert-eagle (number 24) with 30,000 bullets.
The player has two weapons, but can only use one at the same time. To define which is in his hand, you have to use the "01B9" opcode. In this case the player has the desert-eagle in his hands.

Now we will give an actor a weapon:
CODE
01B2: give_actor 10@ weapon 31 ammo 30000 // Load the weapon model before using this
01B9: set_actor 10@ armed_weapon_to 31

So the actor gets a M4 (number 31) with 30,000 bullets. It will also be in his hands.

Both actors (Player and other) have weapons. Now we will give the actor an assignment: kill the player. We will also set some other properties of the actor.

CODE
05E2: AS_actor 10@ kill_actor $PLAYER_ACTOR

The actor (10@) has to kill the player.

CODE
actor.WeaponAccuracy(10@)= 90

The weapon accuracy of the actor is 90, so the actor will shoot more accurate then when you don't use this opcode.

CODE
actor.Health(10@) = 2000

The actor has a lot of health, so it is more difficult to kill him.

CODE
0350: toggle_actor 10@ maintain_position_when_attacked 1

The actor maintain his position when it's attacked. Otherwise it is possible that he runs away.

Now we have to code the end of the fight. We have to check if the actor is dead:
CODE
repeat
wait 0 ms
until actor.Dead(10@)


Finally we have to set a sound, a text and a reward:
CODE
0394: play_music 1
01E3: show_text_1number_styled GXT 'M_PASS' number 10000 time 5000 style 1  // MISSION PASSED!~n~~w~$~1~
Player.Money($PLAYER_CHAR) += 25000

The first line is for the sound, the second is for the text. This will display after the actor is dead.
We will give a reward of $250000.

Then we have to unload the models, just like we learned in previous tutorials:
CODE
0249: release_model #BFYST
0249: release_model #rocketla
0249: release_model #desert_eagle
0249: release_model #m4


All together (with a stripped file)
  • Indi likes this

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#7

Posted 21 March 2009 - 11:05 PM Edited by Dutchy3010, 25 March 2009 - 10:22 AM.

Cars


Introduction
This tutorial is about an essential part of the game: cars. There are two ways of spawning cars: cargenerators, which spawn multiple times or just cars that spawn once. I will explain both.

Cars that spawn multiple times
Cargenerators are more easy to spawn then cars that spawn once, because you don't have to load and unload the models of the cars. This way the permanent cars in the game are spawned.

The lines for the cargenerator:
CODE
014B: $PARKED_RHINO = init_parked_car_generator #RHINO -1 -1 1 alarm 0 door_lock 0 0 10000 at 2435.302 -1671.848 12.8007 angle 90.0
014C: set_parked_car_generator $PARKED_RHINO cars_to_generate_to 101


We will analyse this code. The first line:
  • $PARKED_RHINO: a variable to which the car is assigned.
  • #RHINO: model name of the car that you want to spawn.
  • First -1: first color of the car. ( -1 = random color )
  • Second -1: secondary color of the car. ( -1 = random color )
  • The 1: the police thinks that it is your car. If it's a 0, then the police knows that you stole it.
  • Alarm 0: the chance that the alarm will sound is 0%. If you make it 50, the chance is 50%.
  • Door_Lock 0: the door isn't locked. Make it 1 and the car is locked.
  • At the end are the x, y and z coords, and the angle of the car.
Second line:
  • $PARKED_RHINO: the variable holding the car that the opcode is meant for.
  • 101: the car will spawn. If you make it a 0, the car won't spawn.
  • You have to do this line always when you are making a cargenerator!
Example in the stripped file

There is another variant, which you can use to give the car a numberplate:
CODE
09E2: $grove = parked_car_generator_w_numberplate #GREENWOO 59 34 0 alarm 0 door_lock 0 0 10000 plate "GROVE4L_" at 2508.4824 -1670.6813 13.3812 angle 0.0
014C: set_parked_car_generator $grove cars_to_generate_to 101

The only differences are the code (09E2 instead of 014B), init_parked_car_generator instead of parked_car_generator_w_numberplate (it doesn't even matter!) and of course the "Plate...". After plate you can write what you want to have on the numberplate.(8 characters)

Cars that spawn once

This looks a lot like spawning an actor:
CODE
//Load models
0247: load_model #GREENWOO
038B: load_requested_models

:MODEL_LOAD
if  
8248:   not model #GREENWOO available
else_jump @MODEL_SPAWN
wait 0 ms
jump @MODEL_LOAD

When you read the other tutorials, I think you don't need any explanation.

CODE
:MODEL_SPAWN
$greenwood = Car.Create(#GREENWOO, 2458.2483, -1659.0264, 13.3047)
Car.Angle($greenwood)= 270.0

With this code you will spawn a Greenwood. The variable ("handle") of the car is "$greenwood". The model name is #GREENWOO (you can look it up in the vehicles.ide). After that are the x, y and z coord. The second line is angle of the vehicle.

CODE
0249: release_model #GREENWOO

Of course we have to release the model.

There are all kind of codes for cars that spawn only once. A few examples:

CODE
0674: set_car_model #GREENWOO numberplate "ABCDEFG"

You can set a special numberplate. You have to use this opcode right before you create the car.

CODE
 0119:   car $greenwood wrecked

You can ask if the car is wrecked, most used in a if-structure. With the $ you have to use the variable (handle) of the car.

CODE
0224: set_car $greenwood health_to 10000

When the number is higher, the car can take more damage. Of course you can vary with this. The greenwood is smoking when the number is 500. When it is 250, one small crash is enough to blow up the car (black smoke). When it is less then 250, the car explode without doing anything. 1000 is a realistic number for the car. The $greenwood is the variable of the car, defined by spawning the car.

CODE
00A6: destroy_car $greenwood

With this car you can remove the car from the game. Just like with actors, a release model doesn't delete the car. For that, you have to use this opcode.

Of course there are many more commands that's got to do with cars, because it is an essential part of the game. So when you want to do something with a car that isn't explained here, and that you don't know how to use it, feel free to ask!

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#8

Posted 21 March 2009 - 11:07 PM Edited by PatrickW, 07 July 2009 - 03:31 PM.

Spheres, icons and markers


First: what are spheres, icons and markers? Icons are the graphics on the radar, markers are the arrows above the heads of the actors or cars. Spheres are the small, red rounds, which for example occur when you are starting the mission.

Create a sphere/icon?
You can create a icon and sphere with 1 command, you can also only create a sphere or an icon. There are icons you will see always, like mission letters, and icons which only occur when you are in the neighbourhood, like ammu-nations. I will explain all of them.

Spheres
CODE
03BC: 10@ = create_sphere_at 2488.5601 -1666.84 13.38 radius 5.0

This is the code for only a sphere. The 10@ is the variable (handle) of the sphere, which you can use later on when you want to do something with the sphere (like destroy it). Then there are x, y and z coords. Finally there is a radius. 1.0 is small, 10.0 is big. Spheres in which you can start a mission, are about size 1.0.


Icon and Sphere that are always visible
CODE
02A7: $ICON_SPHERE_CJ = create_icon_marker_and_sphere 15 at 2488.5601 -1666.84 13.38

15 is the number of the icon you want to display. You can find it in Help > Contents > SCM Documentation > GTA SA > Radar icons. Then you have the 3 coords: x, y and z. $ICON_SPHERE_CJ is the variable which you can use to delete the sphere and the marker

Icon (without sphere) that is always visible
CODE
02A8: $marker03 = create_marker 23 at 658.0068 -1866.3127 4.4537

This is the code for a icon marker without sphere, so you will see it on the radar, but you won't see a red round on that place. First the handle of the marker, the 23 says which icon it has to be. You can look it up in the help files, like said above. In this case, 23 is the Loco Syndicate icon. Finally you have to add the coords.

Icon and Sphere that are only visible when you are in the neighbourhood
CODE
0570: $icon_en_sphere  = create_asset_radar_marker_with_icon 36 at 1837.3643 -1974.4963 12.5469

Like you can see, there are only 2 things changed. The code of the opcode (02A7 > 0570), and the command itself. But the idea is the same: 36 is the number of the icon which you can look up in the help-file list and the coords.

Icon that is only visible when you are in the neighbourhood
CODE
04CE: $icon = create_icon_marker_without_sphere 12 at 2447.3643 -1974.4963 12.5469

I don't think explanation is necessary anymore, by now.

Markers
Markers are arrows above an object, an actor or a car. They are also marked (with a square) on the radar. There are all kind of markers. You have to use one of the following opcodes:
CODE
0187: $m1 = create_marker_above_actor 10@  
0186: $m2 = create_marker_above_car $greenwood
03DC: $m3 = create_marker_above_pickup $weapon
0188: $m4 = create_marker_above_object $object

The first one is to create a marker above an actor, the second one above a car, the third above a pickup and the fourth above an object.

These are different opcodes! Look at the code of the opcode (like 0187), it is different than the others! The first variable is the handle/name of the marker, the second is the handle of the actor/car/pickup or object.

You can do all kind of things with the marker, like make it in different colors:
CODE
0165: set_marker $m1 color_to 1

List of colors:
0= red
1= green
2= light blue
3= white
4= light yellow

Destroy
Finally you have to de destroy the marker/sphere/icon, so that you can't see it any more. You can use the following opcodes for that:
CODE
0164: disable_marker $m1

CODE
03BD: destroy_sphere 1@



For example: an arrow on a Greenwood:
CODE
//Load models
0247: load_model #GREENWOO
038B: load_requested_models

:MODEL_LOAD
00D6: if  
8248:   not model #GREENWOO available
004D: jump_if_false @MODEL_SPAWN            
0001: wait 0 ms
0002: jump @MODEL_LOAD

:MODEL_SPAWN
00A5: $greenwood = create_car #GREENWOO at 2458.2483 -1659.0264 13.3047
Car.Angle($greenwood)= 270.0
0186: $m1 = create_marker_above_car $greenwood
wait 5000 ms
Marker.Disable($m1)

0249: release_model #GREENWOO

So there spawns a Greenwood, with an arrow. After 5000 ms (5 seconds), the arrow will disappear.

The full code in a stripped file

When you worked through all of these tutorials sofar, you can make a short, easy mission! We will do that in the next tutorial!

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#9

Posted 21 March 2009 - 11:08 PM Edited by Dutchy3010, 25 March 2009 - 10:22 AM.

A simple mission


Like I promised, we are going to make a simple mission. First you have to make a good plan for the mission. In our example, we will spawn a car with an arrow above it. When the player gets in the car, the arrow will disappear, and the sphere where you have to drive to will appear. The player will have a baseball bat. The car is almost wrecked, and with the slightest touch it will explode. When the player is in the sphere, there have to spawn an actor, also with a baseball bat. When he sees the player, he will attack. When the actor is killed, the mission is passed, and you will get a text and $10,000. The marker will also disappear.

Of course this isn't a very difficult mission, but you can make it more amusing. For example, when you add a timelimit: you have to drive fast, but may not hit anything! Or that when you get out of the car, there will be another marker on the car. Or that mission failed is when you are dead. But we will start small, with this mission!

We have an idea, so we will begin coding. Like usual, we will start with a stripped file:

Click

First we will load everything and check if it is loaded. The actor (enemy) doesn't have to spawn, the car does. The bat has toi be given to the player, lateron also to the actor. Loading the models and spawning the car:
CODE
:MODEL
thread 'MODEL'
//Load models
0247: load_model #GREENWOO
0247: load_model #bat
0247: load_model #BFYST
038B: load_requested_models

:MODEL_LOAD
if or
8248:   not model #GREENWOO available
8248:   not model #bat available
8248:   not model #BFYST available
else_jump @MODEL_SPAWN            
wait 0 ms
jump @MODEL_LOAD

:MODEL_SPAWN
0674: set_car_model #GREENWOO numberplate "ABCDEFGH"
00A5: $greenwood = create_car #GREENWOO at 2458.2483 -1659.0264 13.3047
Car.Angle($greenwood)= 270.0
0224: set_car $greenwood health_to 250


What are we missing before player has to drive? The player has to get a baseball bat and there has to be a marker above the car:
CODE
0186: $m1 = create_marker_above_car $greenwood
01B2: give_actor $PLAYER_ACTOR weapon 5 ammo 10 // Load the weapon model before using this
01B9: set_actor $PLAYER_ACTOR armed_weapon_to 5


NOw we have to wait for the player to enter the car. What happens next? You have to go to the marked place, where there should be a sphere. The marker of the car has to disappear.
CODE
repeat
wait 0
until Actor.InCar($PLAYER_ACTOR, $greenwood)
Marker.Disable($m1)

0004: $coordinate_x = 2100.9604
0004: $coordinate_y = -1366.8369
0004: $coordinate_z = 23.9844

$coordinate = Marker.CreateIconAndSphere(0, $coordinate_x, $coordinate_y, $coordinate_z)


What can happen while the player is driving to the sphere? The car can be wrecked! When the car is wrecked, there has to spawn a new car in the beginning and the player has to go back to get it. The check has to continu until the player has reached the sphere. This is perhaps a bit more complex code, but when you give it a good look, I'm sure you can make it next time by yourself!
CODE
:MODEL_WRECKED
wait 0 ms          
if  
 0119:   car $greenwood wrecked      
else_jump @MODEL_END            
jump @MODEL_SPAWN

:MODEL_END
wait 0 ms
if
01AE:   car $greenwood sphere 0 near_point $coordinate_x $coordinate_y radius 5.0 5.0 stopped
else_jump @MODEL_WRECKED

This means:
When the car is wrecked, jump @MODEL_SPAWN. Then you will start all over, by spawning a new car with a new arrow. When the car isn't wrecked, it will jump to MODEL_END, the second label. There it will check if the car is in the sphere. You have to make this a loop, because when you are doing it only once, the car won't be at the sphere yet.

So when the car is in the neighbourhood of the sphere (radius 5.0), and the car stopped, then the code may continue. When it isn't, the code goes back to MODEL_WRECKED and check again if the car is wrecked. This continues until the car is in the sphere.

When the player is in the car in the sphere, the sphere and the marker have to disappear. Also you have to spawn an actor with baseball bat, which had to attack the player.
CODE
Marker.Disable($coordinate)
10@ = Actor.Create(CIVFEMALE, #BFYST, 2094.3499, -1342.9515, 23.9844)
Actor.Angle(10@) = 88.44
0187: $marker = create_marker_above_actor 10@
actor.WeaponAccuracy(10@)= 100
actor.Health(10@) = 250
01B2: give_actor 10@ weapon 5 ammo 1000 // Load the weapon model before using this
01B9: set_actor 10@ armed_weapon_to 5
05E2: AS_actor 10@ kill_actor $PLAYER_ACTOR

Every part of this code has been explained already in previous tutorials, if it's unclear you can look it up there.

Finally, when the actor is dead, the mission is passed. You have to play a sound, put a text on the screen and add some money. The marker on the actor has to be destroyed and the models have to be unloaded:
CODE
repeat
wait 0 ms
until actor.Dead(10@)

0394: play_music 1
01E3: show_text_1number_styled GXT 'M_PASS' number 10000 time 5000 style 1 // MISSION PASSED!~n~~w~$~1~
Player.Money($PLAYER_CHAR) += 10000
0249: release_model #GREENWOO
0249: release_model #BAT
0249: release_model #BFYST
Marker.Disable($marker)

Every part of this code has also been explained in previous tutorials.

Don't forget to add the "end_thread" to stop the thread. Then you are finished.
CODE
end_thread


When you put this all together: Click . Now you can try your mission! Perhaps it isn't the most difficult mission, but who cares? You have to start small!

So it shows, that with some simple codes, you can already make a nice mission.
The limitions of the posibilities are not your coding-skills, but your creativity.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#10

Posted 21 March 2009 - 11:09 PM Edited by Dutchy3010, 25 March 2009 - 10:21 AM.

You have now learned the basics of SCM-coding. The following tutorials will each explain more advanced aspects of SCM-modding. After working through all tutorials you should be able to combine those building blocks into rather advanced missions. When you have wishes for a tutorial, you can always send me a PM!

Good luck and above all: have fun!

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#11

Posted 21 March 2009 - 11:11 PM Edited by Dutchy3010, 25 March 2009 - 10:23 AM.

A complete mission ( Rockstar Style )


In this tutorial, I will explain you how to make a real mission, just like Rockstar. So it will not be in a simple thread like the previous tutorials.

First we will show the structure and specifics, then we will give templates for the different parts and explain these. Finally we will show you a mission based on this templates.

Structure
Missions like they are made by Rockstar have other behaviours then normal threads. First the code is only read when the mission starts, so there is only 1 mission at the same time active. Another big difference is how the code react when a player is Busted or Wasted.
In a normal thread, you always have to check if the player is defined, but this isn't necessary in a mission-structure. When the player is Wasted or Busted, the SCM-engine will generate a "return"-opcode. So you don't have to check it during the mission. The framework which you should use to create a reak mission, will be explained later on in this mission.

You will need two parts to make a mission:
  • The mission itself
  • The so called "sniffer"-thread, which checks the starting conditions of the mission and whether the player triggers a mission (like walk into a sphere).
For both parts we will give you a template, which you can use for your own project.

Sniffer-thread template

The sniffer-thread will be started, when the missions are available. This can be directly from the start of the game, or when you passed other missions. This template contains the code for a sniffer-thread with two missions, but you can easily extend this with many more missions, which can be played after each other.
CODE

// $TUT_MISSION_PASSED has to be cleared (0).
// When you pass the mission, you have to raise $TUT_MISSIONS_PASSED.
:TUT_MISSIONS_SNIFFER
thread "TUT_SNIFF"
$MARKER_SWEET_HOUSE = Marker.CreateIconAndSphere(42, <<xcoord>>, <<ycoord>>, <<zcoord>>)
repeat
 wait $DEFAULT_WAIT_TIME
 if and
   player.Defined($PLAYER_CHAR)
   $ONMISSION == 0
 then
   if and
     00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere <<xcoord>>, <<ycoord>>, <<zcoord>> radius 1.0 1.0 2.0 on_foot
     Player.Controllable($PLAYER_CHAR)
     $TUT_MISSIONS_PASSED == 0
   then
     $ONMISSION = 1
     00BA: show_text_styled GXT 'INTRO_1' time 1000 style 2  
    start_mission 1  
   end
   
   if and
     00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere <<xcoord>>, <<ycoord>>, <<zcoord>> radius 1.0 1.0 2.0 on_foot
     Player.Controllable($PLAYER_CHAR)
     $TUT_MISSIONS_PASSED == 1
   then
     $ONMISSION = 1
     00BA: show_text_styled GXT 'INTRO_2' time 1000 style 2  
    start_mission 2  
   end
 end
until $TUT_MISSIONS_PASSED == 2
marker.Disable($MARKER_SWEET_HOUSE)
end_thread

This thread will make a sphere on the specified location, where you have to start the missions. It will wait until the player walks into the sphere and stop running when both missions are passed.
While $TUT_MISSIONS_PASSED is 0, none of the missions is passed. When the player walks into the sphere, it will start the first mission.
After the player passed the first mission, $TUT_MISSIONS_PASSED will be 1. Then the sniffer-thread will wait until the player walks into the sphere.
After the player passed the second mission, $TUT_MISSIONS_PASSED will be 2, so the loop will be ended, the sphere will be destroyed and the thread will end.

Mission template

CODE
//-------------Mission 1---------------
// Mission wrapper
:TUT_MISSION_1
thread 'TUT 1'
gosub @TUT_MISSION_1_MAIN
if wasted_or_busted
then
 gosub @TUT_MISSION_1_FAIL
end
gosub @TUT_MISSION_1_CLEANUP
end_thread

//-------------------------------------
:TUT_MISSION_1_MAIN


//-------------------------------------
:TUT_MISSION_1_PASSED
Player.Money($PLAYER_CHAR) += 10000
01E3: show_text_1number_styled GXT 'M_PASS' number 10000 time 5000 style 1 // MISSION PASSED!~n~~w~$~1~
// This subroutine is executed when the mission is passed.
// Give the rewards, and make new missions available if needed.

return

//-------------------------------------
:TUT_MISSION_1_FAIL
00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1  // ~r~MISSION FAILED!
// This subroutine is executed when the mission fails.
return

//-------------------------------------
:TUT_MISSION_1_CLEANUP
$ONMISSION = 0
// This subroutine is always executed at the end of the mission, regardless the outcome.
// This is the place to unload models etc.
mission_cleanup
return

The first part of the code is called the mission-wrapper, which calls the various parts of the mission with the help of a "gosub". This way there can be an automatic "return" after the player is WASTED or BUSTED. First he calls the main part of the mission. When it comes back, there can be two possible scenarios:[list]
•An automatic "return", because the player is Wasted or Busted. In this case the mission failed will be called, and after that the "cleanup" routine, which cleans everything up.
•The mission is Passed or Failed for other reasons. In this case the mission_passed or mission_failed routine has already been called, so only the clean up has to happen.

Below TUT_MISSION_1_MAIN will follow al the code for the gamneplay of the mission. In the complete example below, you will see how you can call the mission_passed and the mission_failed routines from within the main part, in certain situations.

Example Mission
Based on the above templates we made a simple mission. We will use the things we learned in the earlier tutorials.

The purpose of the mission, which you can start in front of Sweet's house, is to kill an actor. There are two actors, a man and a woman. The woman is an enemy, the man is a friend. When you kill the woman, the mission is passed, but if you kill the man, the mission is failed. When the player is wasted or busted before the woman is killed, the mission will fail too.

Click here to view the complete code

The first part of the code, is the standard part of the stripped SCM file. The only thing you have to add, is for the start of the sniffer-thread:
CODE
00D7: create_thread @TUT_MISSIONS_SNIFFER


Finally we will make a loop in the main-thread (:MAIN_LOOP).

Then there is the sniffer thread, based on the template, but reduced to one mission.

You will see that we defined two missions, the fist (mission 0), we will use, just like Rockstar, for initialising many things. Here you can add cargenerators or, for example, weapon pickups. We will set $TUT_MISSIONS_PASSED to 0, and we will define 3 variables for the mission trigger.

The second mission (mission 1) is set up just like the template. In the main part, the models are loaded, the actors spawned, the weapons assigned and the car spawned, just like in previous tutorials. After that, the code waits in a loop, until $VICTIM is dead. In this case the code will go through the mission_passed routine, and the mission is finished. During the loop it will be checked whether the innocent man is still alive. When that isn't the case, we will jump to the mission_failed routine and the mission will end.

In the mission passed routine, we will set a message on the screen, we will award the player some money and we will raise $TUT_MISSIONS_PASSED.

In the mission failed routine, we will set a message on the screen and we will take away the weapon which we gave to him for this mission.

In the cleanup routine, we clean up every used model, and we will set the wanted-level back to 0.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#12

Posted 21 March 2009 - 11:12 PM Edited by Dutchy3010, 25 March 2009 - 10:21 AM.

Status Bars


This tutorial is about Status Texts, or "bars". You can find them in SA missions like "Drive-By" and "supply-lines".

CODE
:SWEET4_47
Model.Load(#GREENWOO)
038B: load_requested_models

:SWEET4_103
if
  not Model.Available(#GREENWOO)
else_jump @SWEET4_152
wait 0
jump @SWEET4_103

:SWEET4_152
1@ = 2508.16
2@ = -1666.47
3@ = 13.0
4@ = 16.0
0395: clear_area 1 at 1@ 2@ 3@ range 6.0
5@ = Car.Create(#GREENWOO, 1@, 2@, 3@)
0229: set_car 5@ color_to 59 34
Car.Angle(5@) = 4@
Car.Health(5@) = 1450
$health = Car.Health(5@)

This code is just spawning a vehicle, the Greenwood. In addition the health of the car is stored in variable $health.

First I will explain the principle. The bar goes from 0 to 100, it are percentages. The initial health of the car is 1450. Under the 250, the car will be destroyed. So 250 will be the 0 of the bar. When 250 is in the bar 0, then the 1450 is 1200 (1450 - 250). Because you have only from 0 to 100, you have to divide it by 12. You have to code it like this:

CODE
0084: $health2 = $health // integer values and handles
if
 $health2 > 250 // integer values
else_jump @SWEET4_11472
$health2 -= 250 // integer values

:SWEET4_11472
$health2 /= 12 // integer values

Before you can subtract 250, you have to check if it is more then 250. Otherwise it will be a negative number, which can't be in the bar. If the number is beneath the 250, you have to go to the next label (with the jump). If the number is above the 250, you have to subtract the 250.

Then you have to divide the number by 12, so it is a number between the 0 and the 100.

CODE
0151: remove_status_text $health2
03C4: set_status_text_to $health2 1 'SWE4_08'  // CAR HEALTH

This is the code of the "bar". You won't see the "car health" text, because the GXT is not correct, but we will ignore that here. You can place a 0 instead of the 1, but then it will be displayed as number ingame, and it won't be a bar.

CODE
repeat
wait 0 ms
$health = Car.Health(5@)

0084: $health2 = $health // integer values and handles
if
 $health2 > 250 // integer values
else_jump @SWEET4_11472
$health2 -= 250 // integer values

:SWEET4_11472
$health2 /= 12 // integer values

until  $health < 250

0151: remove_status_text $health2

end_thread


We want to make a bar until the car is wrecked, so when the variable is 0, the bar has to disappear.

This is a repeat-wait-until structure, so it has to repeat everything from the repeat-command until the condition is true. The condition is that $health has to be below the 250. He has to check the car health every time, and has to make it a number between 0 and 100. That's why it has to be between the repeat and until. When you don't do that, it will be the same number, because the variable $health2 won't be changed.

So you have to wait until the health of the car is below the 250, then the car will be wrecked. After that you have to let the bar disappear with the 0151 opcode. After that you have to use the end_thread opcode, because else the game will crash if you don't properly end tha thread.

When you add the code in a stripped SCM file, you will see the bar.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#13

Posted 21 March 2009 - 11:18 PM Edited by Dutchy3010, 25 March 2009 - 10:23 AM.

Menus


In this tutorial we will use a screen, to illustrate the text. It is the "Main Panel" of the Design Your Own Mission mod.

user posted image


CODE
0512:  show_permanent_text_box 'CLOTHA'

This is de text at the top on the left, for example "press space to continue". It is the red outlined part of the screen.

CODE
08D4: $MENU = create_panel_with_title 'DUMMY' position 29.0 145.0 width 220.0 columns 1 interactive 1 background 1 alignment 1

With this code you create the menu. You have to give the menu a name (variable/handle), so you can do things with it later on. Further, you have to give it a title. DUMMY means that you don't want text on that place ingame. If you want that the menu has a title, you have to place a text-id instead of 'DUMMY'. This is the blue outlined part of the screen.

CODE
08DB:  set_panel $MENU column 0 header 'DUMMY' data 'DUMMY' 'DUMMY' 'DUMMY'  'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY'

First you have to give it a header, the yellow outlined part. Then, you can add things that the player can choose. One of those things is the pink outlined part of the screen.

There are two ways to go on from here, but I will explain the easiest. I have to admit that I use this way most of the time.

CODE
repeat
wait  0 ms
if 00E1:   player 0 pressed_key 15
then
 08DA: remove_panel $MENU
 03E6: remove text box
 jump @END
end
until 00E1:   player 0 pressed_key 16

This is a repeat-wait-until structure, so the game repeats everything between repeat and until until the the condition is true. In this case, the code will repeat it until the player pressed key 16 (SPACE). When the player presses 15 (ENTER/F) before the player presses SPACE, you want to remove th menu, so you have to remove the panel and the text box. After that, you have to jump to the end (or the beginning, just wat you want).

CODE
08D7: $choice = panel $MENU active_row
08DA: remove_panel $MENU
03E6: remove text box

After the player pressed SPACE, the panel and text box has to be removed. Apart from that, you have to look what the player has chosen. You can do that with the 08D7 opcode, where you check which was the latest active row before you remove the panel.

Finally you have to say what happens when a player chooses. Be aware: it starts with 0, not 1!

CODE
if $choice == 0
then
...
end

if $choice == 1
then
...
end

You have to do the if-then-end structure as many times as you have choices. For example, if you have 5 choices, the code has to be from 0 to 4.

So the total code for a menu:

CODE
08D4: $MENU = create_panel_with_title 'DUMMY' position 29.0 145.0 width 220.0 columns 1 interactive 1 background 1 alignment 1

08DB:  set_panel $MENU column 0 header 'DUMMY' data 'DUMMY' 'DUMMY' 'DUMMY'  'DUMMY'
'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY' 'DUMMY'

repeat
wait  0 ms
if 00E1:   player 0 pressed_key 15
then
               08DA: remove_panel $MENU
 03E6: remove text box
 jump @END
end
until 00E1:   player 0 pressed_key 16

08D7: $choice = panel $MENU active_row
08DA: remove_panel $MENU
03E6: remove text box

if $choice == 0
then
...
end

if $choice == 1
then
...
end


:END
wait 1000
jump @END

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#14

Posted 24 March 2009 - 11:42 PM Edited by Dutchy3010, 25 March 2009 - 10:23 AM.

MPACK


This is a handy "tool" for scm-coders, that's why I will explain how it works.

First notice that this isn't made by modders, but is added by Rockstar. When you use this, you can choose between more then 1 SCM-file at once. This way you don't have to edit the main.scm in "Program Files > Rockstar Games > GTA San Andreas > Data > Script" everytime, so you can play your own savegames in the original game whenever you want. Like you probably know, you have to start New Game when you make a SCM script, and it is annoying to switch between SCM-files. That is the biggest advantage of MPACKs. Another advantage is that you don't have to start San Andreas everytime you make a change in your code, you can simply start new game.

In an MPACK there are 4 files:
  • scr.scm (a file in the same format as the normal main.scm)
  • text.gxt (a normal gxt file)
  • MPACK.dat (Setting the title for this MPACK)
  • scr.txt (The source of scr.scm, only needed during development, not when releasing
In this tutorial I will explain how to make a MPACK. First, the MPACK has to be in the "GTA San Andreas User Files" folder, in "My Documents". The first thing you have to do, is make a folder called "MPACK" in User Files folder. You can make up to a maximum of 9 MPACKS, which have there own folder in the MPACK folder. You can compare it to the slots of the savegames. The number behind "MPACK" says which "slot" the MPACK uses.

user posted image


So in the "MPACKx" folder (x is a number between 1 and 9) have to be 4 files: scr.scm, scr.txt, text.gxt and mpack.dat. The first three will speak for themselves, but the fourth needs some explanation. You have to make a notepad file, in which you have type (for example): "7#Test#". The 7 has to correspond with the "slot" of the MPACK. So when you are using "MPACK7", there has to be a 7. Between the # has to be the name of the MPACK, which you will see in the list ingame. In my case it will be "Test". You have to save this file as "mpack.dat".

user posted image


Now you are ready to go ingame. Run San Andreas, and choose for "New Game". Here you will find a list of your MPACKs, and you can choose the name of the MPACK you need. As you can see, there is "Test" in slot 7.

user posted image


Have fun with coding!

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#15

Posted 24 March 2009 - 11:43 PM Edited by Dutchy3010, 23 June 2009 - 08:14 AM.

Actor Animations and Animation Paths


Introduction
You can assign all kind of animations to actors, just to make the mission more amusing. You have to know two things about the animations: the name of the animation and the name of de IFP-file that contains the animation. You can find a list of it here. The bold and large words are the names of the IFP-file, then the animation names inside that IFP-file will follow. For example, if you want that your actor dances, you have to look in the list and you will find the IFP-file "DANCING". That file contains all kind of animations, in this tutorial we will use "dnce_M_a". Of course you can give the actor also other animations, but please note: I can't guarantee that all of these animations will work!

First we will create an actor, which has to perform the animation. When you worked through the other tutorials of this topic, this won't need any explanation. If you don't understand it, please go back to the tutorial about actors.
CODE
0247: load_model #BFYST
038B: load_requested_models

:MODEL_LOAD
if
8248:   not model #BFYST available
else_jump @MODEL_SPAWN
wait 0 ms
jump @MODEL_LOAD

:MODEL_SPAWN
10@ = Actor.Create(CIVFEMALE, #BFYST, 2491.3635, -1661.4694, 13.3359)
Actor.Angle(10@) =  182.9651

0249: release_model #BFYST


We will first have to load the IFP file, then we have to check if the animation is loaded. After that you have to give her an animation. You can do this with the following opcodes:
CODE
04ED: load_animation "DANCING"

repeat
wait 0
until 04EE:   animation "DANCING" loaded

0605: actor 10@ perform_animation_sequence "DNCE_M_A" IFP_file "DANCING" 10.0 loop 1 0 0 0 time -1 // versionA

Sometimes you don't have to load the animation, but I have to admit that I don't know when. I've seen many bugged scripts because they didn't load the animation, so my advice is to load it always. What do you have to lose? Better a few lines to much, than a bugged script!
Opcode 0605 is exactly the same as opcode 0A1A and 0812. So it doesn't matter which opcode you choose. I will only explain the most important parts of this opcode. Feel free to experiment by changing the other parameters.
First you have to say which actor you will give an animation, in our case that will be 10@, which we just created.
The first integer number, the only 1 in the opcode above, will indicate how many times the animation has to repeat itself. When you make it a 0, the animation will only be done once. When you make it a 1, it will repeated.
After time you will see "-1". This means that the actor will perform the animation until there is an opcode that says that it has to stop. You can change it into a number like 4000, which means that the animation had to continue for 4 seconds.

There is also a variant on the opcode:
CODE
0829: actor 10@ perform_animation "DNCE_M_A" IFP_file "DANCING" 4.0 time 4000 and_dies

In my opinion this is a bit stupid, but you can give it a try. After the actor performed it for 4 seconds, it will die. But it doesn't die like you would espect, it only freezes. There will be blood on the ground, but the actor is still standing, frozen. You can walk through it, and only if you kick the actor it will lay down. Funny to see, though.

CODE
0804: AS_actor 10@ walk_to 2498.1187 -1671.9563 13.3427 angle 0.0 radius 0.2 animation "GRLFRD_KISS_03" IFP_file "BD_FIRE" 4.0 LA 0 LX 0 LY 0 LF 0 LT -1
0804: AS_actor 10@ walk_to 2498.1187 -1671.9563 13.3427 angle 0.0 radius 0.2 animation "DNCE_M_A" IFP_file "DANCING" 4.0 LA 0 LX 0 LY 0 LF 0 LT -1

Two examples of one opcode, try it to see the result (you can edit it into the code at the end, if you find it difficult to create something from a stripped file). The actor walks to the supplied coordinates, and then perform the animation. In the first the actor will walk to the point and then give a kiss. To nobody in this case, but of course, you can add another actor next to it. The second will let the actor dance when he is at the location.

CODE
0611:   actor 2@ performing_animation "LRGIRL_IDLE_TO_L0"
8611:   not actor 2@ performing_animation "LRGIRL_BDBNCE"

This opcode is a condition, so you have to use with for example an if-structure. This can be handy if you want to check if the animation is stopped, or if the actor is doing an animation. Here is an example of a code that checks if the animation is stopped:
CODE
0605: actor 10@ perform_animation_sequence "DNCE_M_A" IFP_file "DANCING" 10.0 loop 1 0 0 0 time 3000 // versionA
repeat
 wait 0
until 8611:   not actor 10@ performing_animation "DNCE_M_A"


Yet another opcode for an animation:
CODE
0612: set_actor 10@ animation "DNCE_M_A" paused 1

With this code you can pause an animation. 1 means that it has to start, 0 means that it has to pause. For example we will make an actor with an animation, that has to do it for 1 second, then has to stop for 1 second, and then has to continue.
CODE
0605: actor 10@ perform_animation_sequence "DNCE_M_A" IFP_file "DANCING" 10.0 loop 1 0 0 0 time -1
wait 1000
0612: set_actor 10@ animation "DNCE_M_A" paused 0
wait 1000
0612: set_actor 10@ animation "DNCE_M_A" paused 1


CODE
0605: actor 10@ perform_animation_sequence "DNCE_M_A" IFP_file "DANCING" 10.0 loop 1 0 0 0 time -1
wait 3000
0614: set_actor 10@ animation "DNCE_M_A" progress_to 0.5 // 0.0 to 1.0

This is another opcode, of course we are talking about the 0614 opcode. You can split up every animation. The whole animation is 1.0, it starts at 0.0. So you can say: I want that the actor perform the animation for 3 seconds, and then it has to start over from 0.5. Of course you can also say it has to start from the beginning after some time, then you have to write 0.0.

Release animation
Of course you have to release the animation when you loaded it:
CODE
04EF: release_animation "DANCING"


Animation Paths
CODE
0754: define_new_animation_path
0755: add_animation_path_3D_coord 2496.5234 -1671.363 13.3359 animation "ROADCROSS" IFP_file "PED"
0755: add_animation_path_3D_coord 2499.4873 -1661.1962 13.3587 animation "ROADCROSS" IFP_file "PED"
0755: add_animation_path_3D_coord 2490.1294 -1661.246 13.3359 animation "ROADCROSS" IFP_file "PED"

You first have to define the animation path. You have to start with define new animation path. Then you have to add coordinates. In this example, we will add three coordinates. The Animation and the IFP-file is just like the animations explained at the beginning of the tutorial. You can also use "NONE" instead of "ROADCROSS" and "PED", but then they won't do an animation. The animation will be performed at the coord, so it won't be during the time that the actor walks to the coord.

Then you have to add an actor to the animation path:
CODE
0817: assign_actor 10@ to_animation_path_with_walk_mode 4 route_mode 3

You can just expriment with this opcode. The route-mode:
0-normal order of waypoints, once (ABCD)
1-normal order of waypoints and reverse, once (ABCDCBA)
2-normal order of waypoints plus reversed, looped (ABCDDCBAABCD...)
3-normal order of waypoints, looped (ABCDABCDABCD...)
(Source: this topic)

Finally I will give you the code of a stripped file with an actor that performs an animation: click.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#16

Posted 25 March 2009 - 10:19 AM Edited by Dutchy3010, 20 June 2009 - 11:56 PM.

Pickups


What does this topic really miss!
Indeed, a tutorial about pickups. So that is what this tutorial is all about.

General Pickups
CODE
0213: $HEALTH = create_pickup #HEALTH type 3 at 2490.5598 -1658.3495 13.3528

This is the opcode to create a pickup. First the handle, so you can give it properties (like a marker) later on. Then you have to give a model for the pickups:
  • Health: #HEALTH
  • Armor: #BODYARMOUR
  • Bribe: #BRIBE
  • Info: #INFO
  • Keycard: #KEYCARD
  • Save pickup: #PICKUPSAVE
  • Rhymesbook: #RMB_RHYMESBOOK
  • Briefcase: #BRIEFCASE
  • Clothes: #CLOTHESP
  • Jetpack: #JETPACK
  • Parachute Pack: #PARA_PACK
  • Parachute: #PARACHUTE
After type, you have to say if it is never available (1), once available (3) or multiple times available (15). There are other types, but these are the most common. If you want to see a full list of the types, you can look at this page. In this case, you can only pick up the HEALTH pickup once. The final three parameters are the coordinates.

Weapon Pickups
CODE
0213: $BAT= create_pickup #BAT type 3 at 2490.5598 -1658.3495 13.3528

This opcode, which is the same as the opcode above, can also be used for weapons. But you can't give it ammo, so most of the times it is only used for melee weapons. When you create other weapon pickups, like a minigun, the ammo will be a standard value standard. For a minigun it is 500, for a rocket launcher it is only 4. So you can make all weapon pickups with this opcode, but it is more common to use the following:
CODE
032B: $M4 = create_weapon_pickup #M4 group 15 ammo 150 at 2490.5598 -1658.3495 13.3528

This is almost the same as 0213, but now you have to give it some ammo. So when you are using this opcode, it will spawn a M4 multiple times, with an ammo of 150 at 2490.5598 -1658.3495 13.3528.

Money Pickups
It is also possible to create money pickups, with the following opcode:
CODE
02E1: $MONEY = create_cash_pickup 500 at 2490.5598 -1658.3495 13.3528 permanence_flag 1

$MONEY is, of course, the handle. 500 is the amount of money which the player gets when he picked up the money. After that there are the coordinates. When the flag is 1, the money is permanently. It won't disappear until the player gets it. When the flag is 0, it will disappear after 30 seconds.

Asset Pickups
There are two kind of asset pickups: available and unavailable assets. The available can be bought by the player, the unavailable not yet.
CODE
0517: $asset = create_unavailable_asset_pickup 'PROP_4' at 2490.5598 -1658.3495 13.3528  
0518: $asset2 = create_available_asset_pickup 'PROP_3' at 2489.5598 -1658.3495 13.3528  price 1

The first is the unavailable asset. When the player is near the pickup, the text 'PROP_4" will be displayed (You cannot buy this property yet.).
The second is the available asset. Behind price you have to fill in the price of the property.

Collectables Pickups
You can create a horseshoe, a photo opportunity or a oyster with the following opcodes. I added a weapon pickup for the camera, because otherwise you can't see the photo opportunity when you are checking it ingame.
CODE
032B: $camera = create_weapon_pickup #CAMERA 15 ammo 50 at 2488.5598 -1663.3495 13.3528  
0959: $horse = create_horseshoe_at 2490.5598 -1658.3495 13.3528
0958: $photo = create_photo_at 2489.5598 -1658.3495 13.3528
095A: $oyster = create_oyster_at 2488.5598 -1658.3495 13.3528


Other opcodes concerning pickups
CODE
0213: $BAT = create_pickup #BAT type 3 at 2490.5598 -1658.3495 13.3528
03DC: 1@ = create_marker_above_pickup $BAT

With this opcode you can create a marker above the pickup, just like we did in the tutorial about markers and spheres.

CODE
0214:   pickup $BAT picked_up
8214:   not pickup $BAT picked_up

These opcodes are conditions. You can use it to check if the player picked up the pickup. For example:
CODE
0213: $BAT = create_pickup #BAT type 3 at 2490.5598 -1658.3495 13.3528
repeat
wait 0
until 0214:   pickup $BAT picked_up


Sometimes you want to destroy the pickup. You can do it like this:
CODE
0213: $BAT = create_pickup #BAT type 3 at 2490.5598 -1658.3495 13.3528
wait 2000
0215: destroy_pickup $BAT

So the pickup will be created, and after 2 seconds, it will disappear.

Now we will make a small script. First we will spawn three pickups: An asset for 100 dollar, a health and a minigun weapon pickup. Above the minigun weapon pickup, there has to be a marker. After the player picked up the minigun, the marker has to disappear, and there has to spawn an actor. The actor also holds a minigun, and we will set her health and accuracy. The actor will try to kill the player, and the player has to kill the actor. When the player kills the actor, he will get 100 dollar, and he can buy the asset. Then we will display a text.

First we are going to spawn three pickups and a marker above the minigun:
CODE
0518: $ASSET = create_available_asset_pickup 'PROP_3' at 2487.5598 -1658.3495 13.3528  price 100
0213: $HEALTH = create_pickup #HEALTH type 3 at 2484.5598 -1658.3495 13.3528
032B: $MINIGUN = create_weapon_pickup #MINIGUN group 15 ammo 5000 at 2490.5598 -1658.3495 13.3528
03DC: 1@ = create_marker_above_pickup $MINIGUN


Now we have to wait until the player picked up the minigun, and then we have to let the marker disappear and create the actor (with some properties):
CODE
repeat
wait 0
until 0214:   pickup $MINIGUN picked_up

0164: disable_marker 1@

0247: load_model #BFYST
0247: load_model #MINIGUN
038B: load_requested_models

:MODEL_LOAD
if and
8248:   not model #BFYST available
8248:   not model #MINIGUN available
else_jump @MODEL_SPAWN
wait 0 ms
jump @MODEL_LOAD

:MODEL_SPAWN
10@ = Actor.Create(CIVFEMALE, #BFYST, 2488.5601, -1680.84, 13.3438 )
01B2: give_actor 10@ weapon 38 ammo 500 // Load the weapon model before using this
02E2: set_actor 10@ weapon_accuracy_to 90
0223: set_actor 10@ health_to 1000


The actor has to attack the player. After that, the code has to wait until the player kills the actor. As a reward, the player will get 100 dollar.
CODE
05E2: AS_actor 10@ kill_actor $PLAYER_ACTOR

repeat
wait 100
until 0118:   actor 10@ dead

0109: player $PLAYER_CHAR money += 100


Finally we will check if the asset is picked up, and we will display a text.
CODE
repeat
wait 100
until 0214:   pickup $ASSET picked_up

00BC: show_text_highpriority GXT 'MCAT07E' time 3000 flag 1


The complete code in a stripped main.scm!

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#17

Posted 25 March 2009 - 06:18 PM Edited by Dutchy3010, 15 May 2009 - 02:10 PM.

Texts


In this tutorial we will look into texts. There are all sort of texts: big, small, bold, colored...

Display Text
CODE
00BA: show_text_styled GXT 'BEEFY' time 3000 style 1

You have to use the name of the GXT entry between the ''. Behind time you have to put the amount of time which the text needs to be displayed. There are 7 styles, which you can fill in behind style:
  • 1: In the middle, just like when you passed a mission or won a race.
  • 2: At the bottomright, just like when you start a mission.
  • 3: White text in the middle, just like when you are wasted or busted.
  • 4: Style 1, but then smaller.
  • 5: Same as style 4, but a bit higher on the screen.
  • 6: Small white text in the middle of the screen.
  • 7: Light blue text on top of the screen.
Beware, 0 isn't a style, and the game will crash!

White text along the bottom of the screen is displayed using other opcodes. There is a difference between highpriority and lowpriority opcodes. The highpriority will replace the lowpriority. For example:
CODE
00BB: show_text_lowpriority GXT 'IE23' time 3000 flag 1
wait 1000
00BC: show_text_highpriority GXT 'MTIME3' time 3000 flag 1

So here 'IE23' will first be displayed, but after 1 second it will be replaced by 'MTIME3'.

You can clear the texts with the following opcodes:
CODE
00BE: text_clear_all
03D5: remove_text 'MTIME3'



Display texts with number
CODE
01E3: show_text_1number_styled GXT 'BB_15' number 500 time 5000 style 1

With this opcode you can add a number to the text. In this case, your new high score will be 500. The styles are exactly the same as mentioned above. But be ware: you have to use gxt entries that are compatible with numbers! I will get back on this in the following tutorial, when we are going to add new texts.

There are more opcodes about this. I think they will speak for itself:
CODE
01E4: show_text_1number_lowpriority GXT 'HJ_IS' number $var time 2000 flag 1  
036D: show_text_2numbers_styled GXT 'TX_SEQ' numbers $var $var2 time 5000 style 5
02FD: show_text_2numbers_lowpriority GXT 'BB_05' numbers $var $var2 time 5000 flag 1  
02FF: show_text_3numbers GXT 'WHEEL02' numbers $var $var2 $var3  time 3000 flag 1  
0302: show_text_4numbers GXT 'WHEEL01' numbers $var $var2 $var3 $var4  time 3000 flag 1
0303: show_text_4numbers_highpriority GXT 'QUAR_P6' numbers $var $var2 $var3 $var4  time 10000 flag 1  
0308: show_text_6numbers GXT 'HJSTAT' numbers $var $var2 $var3 $var4 $var5 time 5000 flag 5  



Display text boxes

CODE
03E5: show_text_box 'HELP101'

This is the 'normal' textbox. It will disappear after some time. A textbox is a black square in the left upper corner with white letters.

CODE
0512: show_permanent_text_box 'HOSP_1'

This is a permanent text box, so it will be displayed until an opcode is executed which removes it. That is the following opcode:
CODE
03E6: remove_text_box


For example:
CODE
0512: show_permanent_text_box 'HOSP_1'
wait 1000
03E6: remove_text_box

With this opcode you will display a text box, and will remove it after 1 second.

CODE
0513: show_text_box_1number 'SLOT_02' number 2  

This is an opcode for a text box with a number in the text, in this case the value 2.

There are three opcodes which check if the text box exist:
CODE
0A2A:   text_box 'SGPUNT' displayed
08FE:   text_box_displayed
88FE:   not text_box_displayed


CODE
0989: set_text_boxes_width 500

With this opcode, you can edit the width of the textbox. To see the difference, you can use the code below:
CODE
0512: show_permanent_text_box 'HOSP_1'
wait 1000
0989: set_text_boxes_width 500


Drawn Textes
CODE
033E: set_draw_text_position 300.0 200.0 GXT 'BJ_PUSH'

This is an example of a drawn text. You can give it the position you want, by editing the 300.0 and the 200.0. The 300.0 is horizontal, the 200.0 is vertical.
A drawn text is normally a small white text. But you can change this with the opcodes 133F and 1340. You can only remove it with the following code:
CODE
03F0: enable_text_draw 0


You can also make a drawn text with numbers:
CODE
045A: draw_text_1number 320.0 155.333 GXT 'WINNER' number 1000
045B: draw_text_2numbers 320.0 390.0 GXT 'TIME' numbers 20 30

I think these codes speak for itself.


Other text opcodes
CODE
0A19: display_zone_text 'MARKS'

With this opcode you can display a text just like the ones when you enter a zone. For example Saint Mark's.

CODE
09C1: add_next_text_to_brief_history 1

With this opcode you can add a text to "brief" in main menu, so a player can look back. A 1 means that it has to be stored in the brief option, 0 means that it doesn't have to. You have to put this code before the opcode which you are using to display texts. For example:
CODE
09C1: add_next_text_to_brief_history 1
00BB: show_text_lowpriority GXT 'IE23' time 3000 flag 1



When you want to see an example of a text in a short mission, you can look to the previous tutorial, which contains an application of the texts.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#18

Posted 15 May 2009 - 02:10 PM Edited by Dutchy3010, 21 June 2009 - 07:12 PM.

Custom Save Point


In this tutorial, we are going to make an easy custom save point. Be aware, that it doesn't work that well in MPACK. So if you want to make use of the custom save point in scm, then use the main.scm and no MPACK.

Action Plan:
1) Create pickup and check if the player picked it up.
2) Show save screen and wait until the save is done
3) Clean up

1) Create pickup and check if the player picked it up
This isn't too hard. If you followed the other tutorials in this topic too, you will be able to make it yourself. You can make it as follow:
CODE
$save_x = 2488.6506
$save_y = -1660.9884
$save_z = 13.3359

0395: clear_area 1 at $save_x $save_y $save_z range 1.0
$SAVE_PICKUP = Pickup.Create(#PICKUPSAVE, 3, $save_x, $save_y, $save_z)

repeat
   wait 100
until 0214:   pickup $SAVE_PICKUP picked_up

If you don't know what I'm doing here, I suggest that you take another look at this tutorial. wink.gif

2) Show save screen and wait until the save is done
You can show the save screen with one single opcode:
CODE
03D8: show_save_screen


However, we have to do some other things before using this opcode, though. First, we have to set the $ONMISSION variable on 1, so the player doesn't get phonecalls and things like that. Second, we have to make sure that the player can't move, it would look strange when the actor is at a total different place when the save is done.
CODE
$ONMISSION = 1 // integer values
Player.CanMove($PLAYER_CHAR) = False


We have to wait until the save is done. Although you can't find a "save_done" in the opcode search tool or in the main.scm (you can find 83D9 (not save_done) though), it is possible and easy to use.
CODE
repeat
   wait 100
until 03D9:   save_done


Combining these fragments, the second part of the code becomes:
CODE
$ONMISSION = 1 // integer values
Player.CanMove($PLAYER_CHAR) = False
03D8: show_save_screen

repeat
   wait 100
until 03D9:   save_done


3) Clean up
What do we have now? We have the pickup which has to be picked up. Then a save screen will show up, and the code has to wait until the player finished the saving. You don't have to do anything about that saving, that is hardcoded. In this last part, we will fix the camera, and check if the player isn't near the save pickup anymore. Next to that, we have to destroy the pickup.

CODE

Pickup.Destroy($SAVE_PICKUP)
repeat
   wait 100
until Player.Defined($PLAYER_CHAR)

Camera.Restore_WithJumpCut
Camera.SetBehindPlayer
Player.CanMove($PLAYER_CHAR) = True

$ONMISSION = 0

repeat
wait 0
until 80EC:   not actor $PLAYER_ACTOR 0 near_point 2488.6506 -1660.9884 radius 2.0 2.0

jump @SAVE_LOOP

The first line is to destroy the pickup. At the end, we will jump to the beginning (after waiting until the player isn't on that place any more), so the pickup will be created again. Then we restore the camera. The player has to be able to move again. Finally, we have to put the $ONMISSION variable back to 0, else it isn't possible to do another mission or get calls.

The full code of a custom save point thread:
CODE
:SAVE
thread "SAVE"

// location of the save pickup
$save_x = 2488.6506
$save_y = -1660.9884
$save_z = 13.3359

:SAVE_LOOP
0395: clear_area 1 at $save_x $save_y $save_z range 1.0
$SAVE_PICKUP = Pickup.Create(#PICKUPSAVE, 3, $save_x, $save_y, $save_z)

repeat
   wait 100
until 0214:   pickup $SAVE_PICKUP picked_up

$ONMISSION = 1 // integer values
Player.CanMove($PLAYER_CHAR) = False
03D8: show_save_screen

repeat
   wait 100
until 03D9:   save_done
 
Pickup.Destroy($SAVE_PICKUP)

repeat
   wait 100
until Player.Defined($PLAYER_CHAR)

Camera.Restore_WithJumpCut
Camera.SetBehindPlayer
Player.CanMove($PLAYER_CHAR) = True

$ONMISSION = 0

repeat
   wait 0
until 80EC:   not actor $PLAYER_ACTOR 0 near_point $save_x $save_y radius 2.0 2.0

jump @SAVE_LOOP

end_thread


Click here to view the code in a stripped main.scm.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#19

Posted 15 June 2009 - 02:16 PM

Cutscenes


In this tutorial, I'm going to explain a basic cutscene. It is pretty difficult to tell you how to do it, because every single cutscene is different. However, I think you would be able to learn it when I explain a simple cutscene. So I coded a simple cutscene myself, and I will show you what it means. Keep in mind that you have to test almost every code you are doing. You have to put waits between it to make it a better story, you have to test for the camera, etcetera.

Example cutscene
Basically, a cutscene is a lot of opcodes which don't need input from the player. If the player can't do anything, and just have to watch something, it's a cutscene. Keep this in mind in the tutorial. We will start with the full code of the cutscene I scripted:

CODE
//---------------------
//Start Cutscene
//---------------------
:CUTSCENE
03BC: $begin = create_sphere_at 2489.0088 -1658.4554 13.3519 radius 1.0

repeat
wait 0
until 00FE:   actor $PLAYER_ACTOR sphere 0 in_sphere 2489.0088 -1658.4554 13.3519 radius 1.0 1.0 1.0

Player.CanMove($PLAYER_CHAR, false)

//---------------------
//Load Models
//---------------------

model.Load(#army)
model.Load(#patriot)
model.load(#m4)
023C: load_special_actor 'SWEET' as 1 // models 290-299
023C: load_special_actor 'KENDL' as 2 // models 290-299
023C: load_special_actor 'CESAR' as 3 // models 290-299
038B: load_requested_models

:load_check
wait 0
if and
model.available(#army)
model.available(#patriot)
model.available(#m4)
023D:   special_actor 1 loaded
023D:   special_actor 2 loaded
023D:   special_actor 3 loaded
else_jump @load_check

//---------------------
//Spawn everything in fade
//---------------------

fade 0 500
repeat
wait 0
until 816B:   not fading
02A3: enable_widescreen 1
03BD: destroy_sphere $begin

Car.Create($car, #patriot, 2503.0217, -1672.2841, 13.3594)
Car.Angle($car) = 82.3844

0129: 1@ = create_actor_pedtype 23 model #army in_car $car driverseat
01C8: 2@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 0
01C8: 3@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 1
01C8: 4@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 2

01B2: give_actor 1@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 2@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 3@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 4@ weapon 31 ammo 60 // Load the weapon model before using this


009A: 5@ = create_actor_pedtype 4 model #SPECIAL01 at 2479.1279 -1680.2635 13.338
Actor.Angle(5@) = 359.6866
009A: 6@ = create_actor_pedtype 4 model #SPECIAL02 at 2477.7996 -1678.5049 13.3386
Actor.Angle(6@) = 266.6259
009A: 7@ = create_actor_pedtype 4 model #SPECIAL03 at 2479.3577 -1676.2166 13.337
Actor.Angle(7@) = 176.3852
00A1: put_actor $PLAYER_ACTOR at 2480.9185 -1678.2045 13.3404
Actor.Angle($PLAYER_ACTOR) = 98.7008

04ED: load_animation "GANGS"

repeat
wait 0
until 04EE:   animation "GANGS" loaded

0605: actor 5@ perform_animation_sequence "PRTIAL_GNGTLKA" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 6@ perform_animation_sequence "PRTIAL_GNGTLKB" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 7@ perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor $PLAYER_ACTOR perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1

Camera.SetPosition(2472.7151, -1681.5944, 16.9041, 0.0, 0.0, 0.0)
Camera.PointAt(2494.2827, -1669.9541, 13.3359, 2)
wait 2000
00A7: car $car drive_to 2487.1799 -1670.8324 13.3359
fade 1 500
repeat
wait 0
until 816B:   not fading

//---------------------
//Behaviour after fade
//---------------------

repeat
wait 10
until 01AE:   car $car sphere 0 near_point 2487.1799 -1670.8324 radius 3.0 3.0 stopped

0160: set_camera_point_at 2494.2827 -1669.9541 9.3359 mode 2

05CD: AS_actor 1@ exit_car $car
05CD: AS_actor 2@ exit_car $car
05CD: AS_actor 3@ exit_car $car
05CD: AS_actor 4@ exit_car $car

0635: AS_actor 1@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 2@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 3@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 4@ aim_at_actor $PLAYER_ACTOR -1 ms

074D: AS_actor $PLAYER_ACTOR turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 5@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 6@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 7@ turns_to_and_look_at_actor 1@ timelimit -1

wait 2500

05C4: AS_actor $PLAYER_ACTOR hands_up -1 ms
05C4: AS_actor 5@ hands_up -1 ms
05C4: AS_actor 6@ hands_up -1 ms
05C4: AS_actor 7@ hands_up -1 ms

wait 5000

//---------------------
//Cleanup
//---------------------

fade 0 500
repeat
wait 0
until 816B:   not fading

00A6: destroy_car $car
009B: destroy_actor 1@
009B: destroy_actor 2@
009B: destroy_actor 3@
009B: destroy_actor 4@
009B: destroy_actor 5@
009B: destroy_actor 6@
009B: destroy_actor 7@

0687: clear_actor $PLAYER_ACTOR task
Player.CanMove($PLAYER_CHAR, true)
0373: set_camera_directly_behind_player
02EB: restore_camera_with_jumpcut
02A3: enable_widescreen 0

wait 2000

fade 1 500
repeat
wait 0
until 816B:   not fading


Story and start
The story of this small cutscene is: there is a Patriot with 4 army actors in it. There are also 4 special actors: Sweet, Kendl, Cesar and CJ. Those special actors are talking to each other (with an animation). The Patriot has to drive towards the 4 special actors, and the army guys have to exit the car. Then the 4 special actors have to turn towards the army guys. Those army guys have all a M4, and aim at the group of 4 special actors. Finally the 4 special actors have to surrender.

We will make a sphere in which the player has to walk before the cutscene starts. It would be strange if the player can move CJ during the cutscene, that is why we make sure he can't move. After that we will load everything we need in the cutscene itself. I think I don't have to explain any of this.
CODE
//---------------------
//Start Cutscene
//---------------------
:CUTSCENE
03BC: $begin = create_sphere_at 2489.0088 -1658.4554 13.3519 radius 1.0

repeat
wait 0
until 00FE:   actor $PLAYER_ACTOR sphere 0 in_sphere 2489.0088 -1658.4554 13.3519 radius 1.0 1.0 1.0

Player.CanMove($PLAYER_CHAR, false)

//---------------------
//Load Models
//---------------------

model.Load(#army)
model.Load(#patriot)
model.load(#m4)
023C: load_special_actor 'SWEET' as 1 // models 290-299
023C: load_special_actor 'KENDL' as 2 // models 290-299
023C: load_special_actor 'CESAR' as 3 // models 290-299
038B: load_requested_models

:load_check
wait 0
if and
model.available(#army)
model.available(#patriot)
model.available(#m4)
023D:   special_actor 1 loaded
023D:   special_actor 2 loaded
023D:   special_actor 3 loaded
else_jump @load_check


Fade and spawns
To start, you have to use some fade, because else you would see everything spawn and that isn't nice. So first you have to give a fade, and wait until it isn't fading any more (else you will see things spawn because the screen isn't black yet). After that loop, you have to spawn everything you want.
CODE
//---------------------
//Spawn in fade
//---------------------

fade 0 500
repeat
wait 0
until 816B:   not fading
02A3: enable_widescreen 1
03BD: destroy_sphere $begin

Car.Create($car, #patriot, 2503.0217, -1672.2841, 13.3594)
Car.Angle($car) = 82.3844

0129: 1@ = create_actor_pedtype 23 model #army in_car $car driverseat
01C8: 2@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 0
01C8: 3@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 1
01C8: 4@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 2

01B2: give_actor 1@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 2@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 3@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 4@ weapon 31 ammo 60 // Load the weapon model before using this


009A: 5@ = create_actor_pedtype 4 model #SPECIAL01 at 2479.1279 -1680.2635 13.338
Actor.Angle(5@) = 359.6866
009A: 6@ = create_actor_pedtype 4 model #SPECIAL02 at 2477.7996 -1678.5049 13.3386
Actor.Angle(6@) = 266.6259
009A: 7@ = create_actor_pedtype 4 model #SPECIAL03 at 2479.3577 -1676.2166 13.337
Actor.Angle(7@) = 176.3852
00A1: put_actor $PLAYER_ACTOR at 2480.9185 -1678.2045 13.3404
Actor.Angle($PLAYER_ACTOR) = 98.7008

04ED: load_animation "GANGS"

repeat
wait 0
until 04EE:   animation "GANGS" loaded

0605: actor 5@ perform_animation_sequence "PRTIAL_GNGTLKA" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 6@ perform_animation_sequence "PRTIAL_GNGTLKB" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 7@ perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor $PLAYER_ACTOR perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1

Camera.SetPosition(2472.7151, -1681.5944, 16.9041, 0.0, 0.0, 0.0)
Camera.PointAt(2494.2827, -1669.9541, 13.3359, 2)
wait 2000
00A7: car $car drive_to 2487.1799 -1670.8324 13.3359
fade 1 500
repeat
wait 0
until 816B:   not fading

We also have to give the actors an animation, because it has to look like they are having a conversation. Then perhaps the most difficult about cutscenes: the camera. There isn't an easy way to get the camera the way you want it. I just used a jetpack and then the coords tool in Sanny Builder. You have to test it a lot, so it will become the way you want it.
It is recommended to have a "longer" wait in the fade, so you won't see anything spawn after the fade. But the car drive to has to be behind that wait, because else the car will be on his destination before the player can see something again! Of course we have to lift the fade at the end.

Behaviour after fade
CODE
//---------------------
//Behaviour after fade
//---------------------

repeat
wait 10
until 01AE:   car $car sphere 0 near_point 2487.1799 -1670.8324 radius 3.0 3.0 stopped

0160: set_camera_point_at 2494.2827 -1669.9541 9.3359 mode 2

05CD: AS_actor 1@ exit_car $car
05CD: AS_actor 2@ exit_car $car
05CD: AS_actor 3@ exit_car $car
05CD: AS_actor 4@ exit_car $car

0635: AS_actor 1@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 2@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 3@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 4@ aim_at_actor $PLAYER_ACTOR -1 ms

074D: AS_actor $PLAYER_ACTOR turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 5@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 6@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 7@ turns_to_and_look_at_actor 1@ timelimit -1

wait 2500

05C4: AS_actor $PLAYER_ACTOR hands_up -1 ms
05C4: AS_actor 5@ hands_up -1 ms
05C4: AS_actor 6@ hands_up -1 ms
05C4: AS_actor 7@ hands_up -1 ms

wait 5000

In this code we basically say what the actors have to do after the fade. First we have to wait until the car arrives at his destination. Then we want to change the camera, so it shows what happens better. We want that the army guys exit the car, and that they aim at the player (or someone else of the 4 special actors). We want that the special actors look at the army guys. But we have to wait some time (the wait of 2500 ms), else the special actors will put their hands up before the army guys aim at them. That looks a bit strange. tounge.gif So after the 2500, we want them to put their hands up and the cutscene is finished.

Clean up
Of course we have to clean things up after the cutscene. To make sure nobody will see it, we will first do a fade again. Then we have to destroy the car and the actors. The clear task opcode is because else the Player will keep his hands up after the cutscene. The player also have to be able to move again after the cutscene, so we have to make this opcode true again. The camera has to be behind the player and the the widescreen must be lifted.

CODE
//---------------------
//Cleanup
//---------------------

fade 0 500
repeat
wait 0
until 816B:   not fading

00A6: destroy_car $car
009B: destroy_actor 1@
009B: destroy_actor 2@
009B: destroy_actor 3@
009B: destroy_actor 4@
009B: destroy_actor 5@
009B: destroy_actor 6@
009B: destroy_actor 7@

0687: clear_actor $PLAYER_ACTOR task
Player.CanMove($PLAYER_CHAR, true)
0373: set_camera_directly_behind_player
02EB: restore_camera_with_jumpcut
02A3: enable_widescreen 0

wait 2000

fade 1 500
repeat
wait 0
until 816B:   not fading



Complete code in a stripped main.scm

Other opcodes
There are many other things you can do in a cutscene. You can also use other sort of opcodes, like changing the weather:
CODE
01B6: set_weather 1


Or just use the wanted level opcodes:
CODE
010D: set_player $PLAYER_CHAR wanted_level_to 0
010E: set_player $PLAYER_CHAR minimum_wanted_level_to 2
01F0: set_max_wanted_level_to 6


Another thing you can use in a cutscene, is the "skip cutscene" opcode. If you make a mission, and the player has to redo it, he probably don't want to view the cutscenes again. There is a solution for that. An example of the mission "General Dilemma" (coded by PatrickW and me):
CODE
0707: start_scene_skip_to @GENDIL_3502

CODE
:GENDIL_3502
0701: end_scene_skip
if
08D0:   cutscene_skipped
jf @GENDIL_3523
fade 0 100

:GENDIL_3523

So when the player wants to skip the cutscene, he will go to GENDIL_3502. Apparently in the code the player skipped was a fade, so it need to be done, but only when the cutscene is skipped.

You can use objects, other animations, pickups, texts... As long as you keep in mind that it has to be created and destroyed in the fades, you can do everything! Especially look at the AS opcodes, for example:
CODE
05CD: AS_actor -1 exit_car 22@

These opcodes are all non-playable opcodes, so very useful for cutscenes!

I hope you learned a bit more about cutscenes, if you have any questions or comments about the tutorials or requests for another tutorial, you can post in the reaction topic.

Dutchy3010
  • Dutchy3010

    Female SCM coder!

  • Moderator
  • Joined: 30 Jul 2006
  • Netherlands

#20

Posted 07 September 2013 - 09:25 PM

It has been a while since the last tutorial in this topic. However, I expect that modding GTA San Andreas on the PC will get a boost after the release of GTA V, so that PC gamers will have some of the possibilities of GTA V in San Andreas.

I don't know what kind of tutorial you guys would like to see. Please inform me about which subject you would like to learn. Please keep in mind that this is a mission coding for dummies thread, not mission coding for pro's, so that the tutorials shouldn't be too difficult.

If you have any idea's, please reply in the reaction topic. Thanks a lot. :)
  • cyrilaeshell likes this




3 user(s) are reading this topic

0 members, 3 guests, 0 anonymous users