Quantcast

Jump to content

» «
Photo

[IV | .NET] Writing .net scripts in C#

50 replies to this topic
Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#1

Posted 06 March 2009 - 10:20 PM

Making a .netscripthook mod using C#

Aims: To give you the basics in the C# programming language, so you can write your own scripts for the .netscript hook.

Requirements:
•A C# Devlopment Enviroment (Visual Studio 2005/2008, C# Express Editions 2005/2008). The express editions are free from micrsoft. http://www.microsoft.com/Express/. If you're at a University and the University has registered with MS. Then you can get Studio 2008 Professional for free from MS via the dreamspark system.
- Lastest version of .netscripthook (0.86 at time of writing) -> http://www.gtaforums...howtopic=392325


Intro
So you want to create your own script for GTA IV, this tutorial will introduce you to the basics of programming in C# andcreating a script for the scripthook. Before you can even begin to write scripts . You need to know the programming basics, and how a scripthook script is set up. We'll shall begin with the basics.


Programming with C#

Programming with C# is relatively easy, its syntax is similar to Java, but once you know the basics, loops, if statments, switches etc you can apply these quite easy to other programming languages. Lets start with Data types.

Data types
If you're going to be writing scripts, you're going to need to declare varibles to hold information or data, depending on what you're working with. I'm only going to show you the datatypes that you'll probably be using.

QUOTE

- int, signed 32bit whole number(ranges from -2,147,483,648 to 2,147,483,647)
- float, signed 32bit floating point number(ranges from ±1.5 × 10−45 to ±3.4 × 1038)
- bool, true/false (0 or 1)
- String, text.


The above are the datatypes you'll probably use when writing scripts. When to use these should be self explanatory. If you've got a whole number, and you know its always going to remain whole then use an int. If you've got a decimal number (3.14) then use a float datatype. If you use the wrong type then the complier with complain when compling the script.

To declare and use these datatypes, is fairly straight foward.

CODE

int i;
float f;
bool b;
string s;


This will declare the datatypes with their default values. (note that the ; is a end of line terminator, everyline (except for loops and if statments needs to have a ; at the end of it. You can declare and set the varible value at the same time.

CODE

int i = 25;
float f = 235.5f;
bool b =  false;
string s = "Some text here";


When setting a float value, you need to have a f at the end of the number. This is to designate it as
a float and not a double. Any text for a string varible needs to be enclosed between ""

Arrays
Any datatype can be made into an array. An array is basically a collection of that datatype.

CODE

int[] i = new int[]{0, 1, 6, 9, 2};


That creates an array ints, that can be expanded to include more.

CODE

int[5] i;


That creates an fixed array of size 5.

An item in an array can be accessed by its index number, arrays always start from index 0. So

CODE

i[0] returns 0 first element in the array
i[3] returns 9 forth element in the array


These type of arrays do not have any way of counting how many items are in the array, its up to you to keep track of how many are in it.

Lists

Lists are basically an array, but gives you more control. To use lists within the scripthook, you'll need to add a refence to Systems.Collections.Generic.

CODE

List<int> lInts;
lInts = new List<int>();


That will create a new empty list of ints. To add something to the list, you can use the add method.

CODE

lInts.add(1);
lInts.add(5);


That will add 1 and 5 to the list. You can access a list item, the same as an array.

CODE

lInts[0] is 1;
lInts[1] is 5;


You can remove items from an list by using either remove(item) or removeAt(index). remove will remove an specific item from the list. Whereas removeAt will remove an item at a specifc index.

CODE

lInts.remove(1) //will remove 1 from the list
lInts.removeAt(1) //will remove 5 from the list


Operations on Data types

You can carry out various operations on data types (ie add, subtract, devide etc).

Addition:
floats, ints and strings can all be added together, providing they are of the same datatype.

CODE

int i = 2; //declare & set i to 2
int j = 4; //declare & set j to 4
i = i + j; //Add i and J together.

float f = 2.0f; //declare & set f to 2.0f
float j = 2.2f; //declare & set j to 2.2f
f = f + j; //add f and j together


You can combine a operator and the eqauls together. So the addition statements above, become.

CODE

i += j;
f += j;


You can do the above for any operator, add(+) subtract(-), devide(/) and multiply(*).

CODE

int i = 2; //declare & set i to 2
int j = 4; //declare & set j to 4
i = i * j; //multiply i and J together.

float f = 2.0f; //declare & set f to 2.0f
float j = 2.2f; //declare & set j to 2.2f
f = f * j; //devide f and j together

int i = 2; //declare & set i to 2
int j = 4; //declare & set j to 4
i = i / j; //devide i and J together.

float f = 2.0f; //declare & set f to 2.0f
float j = 2.2f; //declare & set j to 2.2f
f = f / j; //devide f and j together

int i = 2; //declare & set i to 2
int j = 4; //declare & set j to 4
i = i - j; //subtract j from i

float f = 2.0f; //declare & set f to 2.0f
float j = 2.2f; //declare & set j to 2.2f
f = f - j; //subtract j from f


The String datatype, can only be Added together. This joins the two Strings together(concatenate).

CODE

String s1 = "This is a test string s1";
String s2 = "& This is a test string s2";
s1 = s1 + s2; //becomes "This is a test string s1& This is a test string s2"


You can reverse any datatype except for String. By using ! in front of it.

CODE

int i = 2;
i = !i; //this will set i to -2;

bool b = false;
b = !b; //this will set b to true;


Now you know about some basic datatypes you'll be using, we can now move on to the conditonal statements.

Conditional Statements
Conditional statements are statements that can be excuted providing the conditions are met. There are 3 main types of conditional statements.

• if statements
• loops
• switch statements

Lets start with If statements.

IF
If statments can be used to only execute some code, when the condition is met. For example a light. If switch is on, then turn light on, if switch off, turn light off. An if statement is used by the word if and then the condition in brackets. Followed by the code to run enclosed in braces. You can then pecifiy an else condition or just an else, if the condition isn't met.

CODE

bool switch = false;
bool lightOn = false;

if(switch == true)
{
lightOn = true;
} else {
lightOn = false;
}


The above statement reads. if the switch is on(set to true), then switch the light on. Otherwise switch the light off. The else statement will execute if the condition isn't met. Can also check if a number is above or below a certain value etc.

CODE

int i = 40;
if(i < 35) //i less than 35
if(i > 35) //i greater than 35
if(i == 35) // i equal to 35
if(i <= 35) //i less than or eqal to 35
if(i >= 35) //i greater than or equal to 35


You can combine multiple ifs and if elses.

CODE

int i = 40;
if(i < 35)
{
do this;
} else if(i > 35)
{
do this
} else {
do this
}


Lets move on to loops.

loops
There are a couple of different types of loops. for, while, do while and for each. Loops will execute a block of code whilst the condition is matched.

WHILE.
Loops around until the condition is false. Evaluates the condition at the begining of the loop.

CODE

int i = 20;
int a = 0;
while(i < 20)
{
a += i;
i++;
}


The above will execute while i is less then 20.

DO WHILE.
Same as a while loop, except the condition is evaluted at the end of the do cause. So the loop is always run once.

CODE

int i = 20;
int a = 0;
do
{
a += i;
i++;
} while(i < 20)


FOR.
For loops, can be set to run a number of times, and until a certain point.

CODE

int a = 0;
for(int i = 0; i < 20; i++)
{
a += i;
}


FOR EACH.
Used to iterate through an array or collection of objects.

CODE

int a = 0;
int[] i = new int[]{1, 2, 3, 4, 5, 6}
foreach(int b in i)
{
a += b;
}

Thats loops, used to excute a block of code while the condition is met. Lets move on to switch statements.

switch
Switch statements are basically a block of if statements. Each case within a switch can be considered an if statement.

CODE

switch(i)
{
case 0:
//execute if i is 0
do something
break;
case 1:
//execute if i is 1
break;
case 25:
//execute if i is 25
case < 56:
//execute if i less than 56
break;
default:
//execute if none of the above is met
break;
}

Now thats pretty much the basics you'll need to know for C#. If you haven't programmed before, or even if you have the basics. I'd strongly advise people read a C# tutorial. http://www.softsteel...torials/cSharp/ But for now, the above is all you'll need for this tutorial.

Now we're going to move on to the .netscripthook, and how a script is structured. I'm only going to go through some of the baiscs / features that the scripthook has. If you want more examples then you can look at the example scripts that come with the scripthook.

Setting up the IDE for .net scripthook.

Before you can start programming a .net scripthook. You'll need to set up a project for the script you're making, and you'll need add refernces to the hook. So you can access its features.

I'll use both Microsoft Visual Studio 2008 and Microsoft Visual C# Express 2005 as examples. Visual Studio 2008 will be similar to a 2008 Express Edition, and Express 2005 will be similar to Visual Studio 2005. (I cannot use C# Express 2008 since I need Express 2005 for University.)

Right lets setup a project and set up the project for the scripthook. Fire up your development enviroment. You'll see that both express editions 2005 and visual studio 2008 look very similar.

http://www.shadowsre...ts/dotnet/1.jpg

We need to create a project. So under the recent projects box. Click the Create project link.

http://www.shadowsre...ts/dotnet/2.jpg

Note: Since some actions will differ between the two IDEs, I'll refer to Visual Studio 2008 as VS2008, and C# Express 2005 as C#E2005.

We need to set the project for the correct type, so it produces a dll file.

http://www.shadowsre...ts/dotnet/3.jpg

VS2008 - In the projects type box, we need to select a C# project, so expand other languages and choose Visual C#. In the templates window choose Class Library, and give the project a name and click OK.

C#E2005 - Ensure visual C# is selected in the project types, and choose Class Library in the templates
window, and give it a name and click OK.

Now you should be faced with a blank script with some using statements and a constructor.

http://www.shadowsre...ts/dotnet/4.jpg

We need to add using statements and references for the .netscripthook. In the solution explorer, right click References and choose add reference. On the references window. choose Browse, and browse to where you put the .netscripthook. In the folder scripts\For developers\bin\ should be the file ScriptHookDotNet.dll. Choose this file to add the reference. The references folder should of expanded
to show that the scripthook reference has been added. We also need to add the System.Drawing and System.Windows.Forms refrences. These will be under the .net tab.

http://www.shadowsre...ts/dotnet/5.jpg

We now need to add the using statements to the top of the script, so that when we want to use anything from the scripthook. We don't have to put GTA in front of it. We also need the add a using statement for System.Windows.Forms Put: At the bottom of the last using item. put

CODE

using System.Windows.Forms;
using GTA;


So you should have:

QUOTE

VS2008 -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA;

C#E2005 -
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using GTA;


http://www.shadowsre...ts/dotnet/6.jpg

Before we can start to program a script, we need to set the scriptfile up correctly, so that it inherits the methods from the scripthook. Where the class constructor is.

CODE

Public class Class1.


Change Class1 to a much better name, and after the name put : Script . So you should now have.

CODE

public class tutScriptVS : Script
   {
   }


Before we dive in and start writing scripts for the scripthook. We need to know a few basics about the structure of a .netscripthook. As with any script it needs a constructor. This is where you can set varibles, arrays etc and bind events.The constructor is basically the classes name with no return type. So the constructor for my script is:

CODE

public tutScriptVS()
{
}


All the constructors code is enclosed within the braces { }.The constructor is only ran once at the script start up. (as soon as you've loaded the game). So if you want something to run all the time, it needs putting into the scripthooks tick object. To do this you need to bind a method to the tick event. Since we have our script inheriting from the Script object. We can access any properies or methods by using this.

CODE

This.Interval = 5000;
this.Tick += new EventHandler(tickEvent);


The above sets the scripts tick handler, to the tickEvent method. (that we have to create) And sets the Interval to 5000miliseconds. So every 5 seconds, the tick event is run. So any code within the tickEvent method is ran.

When creating a method, it needs a access type (public or private) and a return type. For the tickEvent its void. Meaning it doesn't return anything. And in the parameters for the tickEvent, you need a object sender, and EventArgs e.

CODE

public void tickEvent(object sender, EventArgs e)
{
}


Thats all well, but it's pretty much useless for user interaction. The scripthook comes with a key press handler, so we can have the user interact with our script. Its the same as creating a tick handler, except we use GTA.KeyEventHandler, and set it to the KeyDown method.

CODE

this.KeyDown += new GTA.KeyEventHandler(kdHandler);
public void kdHandler(object sender, GTA.KeyEventsArgs e)
{
}


You can then check for a key press by using an if statement. the KeyEventsArg e, obtains what keys have been pressed. So to check for a press on the with the E key on its own you can use e.Key. Or e.keyWithModifiers to check for a key press using either ctrl, alt or shift. Or specifiy the individual modifier key. System.Windows.Forms.Keys is a list of all the keys in the system. Since we added the using statement for System.Windows.Forms, you can access it by just using Keys.

CODE

//check for single E key press
if(Keys.E == e.Key)

//check for e with any modifier
if(Keys.E == e.KeyWithModifiers)

//check for key press with alt, shift or control
if(Keys.E == e.Control)
f(Keys.E == e.Alt)
f(Keys.E == e.Shift)


Well now we can have a script that runs every x seconds and can detect key presses. What about drawing to the screen? Well we can only draw Text and Rectangles to the screen using the PerFrameDrawing method.

If you wish to draw a quick text message to the screen, you can use the Game.DisplayText("text here") method. This will show a quick message in the top left corner of the screen.

As with the Tick and KeyDown events, the perFrameDrawing needs to be registered to a method.

CODE

this.PerFrameDrawing += new GraphicsEventHandler(gfxDraw)
public void gfxDraw(object sender, GraphicsEventArgs e)
{
}


The methods used for drawing to the screen, are in e.graphics. Lets draw some simple text to the screen. using the e.graphics.DrawText(). We'll see it has 4 possible ways of being called.

CODE

//simply draws a string to the screen at the position given
e.graphics.DrawText(float x, float y, string text)

//same as above, but the size/colour can be controled by the font object
e.graphics.DrawText(float x, float y, string text, font f)

//same as the first one, but can specifiy the color
e.graphics.DrawText(float x, float y, string text, color c)

//all of the above
e.graphics.DrawText(float x, float y, string text, colorc, font f)


The X and Y values are float values, and for the screen range from 0 to 1. So X = 0.5f, and Y = 0.5f would draw something in the center of the screen.

CODE

e.graphics.DrawText(0.5f, 0.5f,"Test Text");


We can change the colour of the text by using the 3rd one, and adding a color value to the end of the call.

CODE

e.graphics.DrawText(0.5f, 0.5f, "Test Text", Color.LammyOrange);


If we really wanted to customise the text, we could use the second one, and create a font object to use within it.

CODE

Font f = new Font();
f.Color = Color.White;
f.Height = 0.25f;

e.graphics.DrawText(0.5f, 0.5f, "Test Text", f)


You can also use the graphics method to draw a rectangle to the screen. There are only two options for e.graphics.DrawRectangle().

CODE

DrawRectangle(RectangleF rect, System.Drawing.Color Color);
DrawRectangle(float CenterX, float CenterY, float Width, float Height, System.Drawing.Color Color);


You'll see that the first one takes a RectangleF as positioning and size. The second one takes the values directly into it. There are differences with the way these two work.The first using the RectangleF will start at the coordinates for the rectangle and draw left and down. Where as the second one, will draw from the center. up, down, left and right.

QUOTE

X ---->
Y
|
|
V


      ^
      |
<--XY-->
      |
      V


So:

CODE

//will draw a rectangle in the centre of the screen, 0.2 high and 0.2 wide, in red.
e.graphics.DrawRectangle(0.5f, 0.5f, 0.2f, 0.2f, Color.Red)

//Same as above, but rather then drawing from the center, it starts at 0.5f and draws down and left.
System.Drawing.RectangleF rf = new System.Drawing.RectangleF(0.5f, 0.5f, 0.2f, 0.2f);
e.graphics.DrawRectangle(rf, Color.CherryRed);


Right, so we have a Tick, KeyDown and Drawing. We're ready to start writing scripts. Lets write a script, that will every 20 seconds, set the players health/amour to max, and if in a vehicle, repair/wash the car. And on a key press, spawn a helicopter.

So in our prepared script, we need a constructor that will setup the Tick, and keyDown events.

CODE

public testScriptVS()
{

//set interval
Interval = 20000;

//bind tick event
this.Tick += new EventHandler(testTick);

//bind keydown event.
this.KeyDown += new GTA.KeyEventHandler(testKeyDown);
}


Right thats our constructor done, it sets the interval and binds the methods. Now we need the tick and key down methods.

CODE

//tick method, ran every 20 secs
public void testTick(object sender, EventArgs e)
{
}

//key down handler
public void testKeyDown(object sender, GTA.KeyEventArgs e)
{
}


Now your script should be looking something like this.

CODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GTA;

namespace tutorialScriptVS
{
   public class tutScriptVS : Script
   {

       public tutScriptVS()
       {
           //set interval
           Interval = 20000;

           //bind tick event
           this.Tick += new EventHandler(testTick);

           //bind keydown event.
           this.KeyDown += new GTA.KeyEventHandler(testKeyDown);

       }

           //tick method, ran every 20 secs
           public void testTick(object sender, EventArgs e)
           {
           }

           //key down handler
           public void testKeyDown(object sender, GTA.KeyEventArgs e)
           {
           }
       
   }
}


Next up is to set the players health and amour to their max, which is 1000. We can access the player using Player.Character.

CODE

//set health and amour
Player.Character.Health = 1000;
Player.Character.Armor = 1000;


Put that inside of the tick method, so now every 20 seconds. The players health and armor is being set to 1000 But we still need to repair/wash the players vehicle. The vehicle can be accessed by using Player.Character.CurrentVehicle. But if we try and access this and the player isn't in a vehicle then the script will crash. We can use the method Player.Character.isInVehicle to test if the player is in a vehicle.

CODE

if (Player.Character.isInVehicle())
{

//repair vehicle
Player.Character.CurrentVehicle.Repair();

//wash vehicle
Player.Character.CurrentVehicle.Wash();
}


So now we have the players health / amour being set to max, and the players car being repaired and washed. Lets allow the player to spawn a helicopter on key press. In the key down method. We can spawn a vehicle by using the World.CreateVehicle method.

[code[
World.CreateVehicle(Model Model, Vector3 Position);
[/code]

The model can be created by using its name. ie "ANNIHILATOR". We can use the Around funciton of the position to get a random position within the players position.

CODE

if(Keys.Q == e.Key)
{

//get position to put vehicle
Vector3 vehPos = Player.Character.Position.Around(10.0f);

//create vehicle
World.CreateVehicle(new Model("ANNIHILATOR"), vehPos);
}


Whoo, your script should be looking something like this.

CODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA;

namespace tutorialScriptVS
{
   public class tutScriptVS : Script
   {

       public tutScriptVS()
       {
           //set interval
           Interval = 20000;

           //bind tick event
           this.Tick += new EventHandler(testTick);

           //bind keydown event.
           this.KeyDown += new GTA.KeyEventHandler(testKeyDown);

       }

           //tick method, ran every 20 secs
           public void testTick(object sender, EventArgs e)
           {
               //set health and amour
               Player.Character.Health = 1000;
               Player.Character.Armor= 1000;

               if (Player.Character.isInVehicle())
               {
                   //repair
                   Player.Character.CurrentVehicle.Repair();
                   //wash
                   Player.Character.CurrentVehicle.Wash();
               }

           }

           //key down handler
           public void testKeyDown(object sender, GTA.KeyEventArgs e)
           {
               if (Keys.Q == e.Key)
               {
                   //get position to put vehicle
                   Vector3 vehPos = Player.Character.Position.Around(10.0f);

                   //create vehicle
                   World.CreateVehicle(new Model("ANNIHILATOR"), vehPos);
               }
           }
       
   }
}


Right, lets set a few project properties, so that when the mod compiles. Its already set the correct file name. In the solution explorer, right click your project and choose properties.

http://www.shadowsre...ts/dotnet/7.jpg

If we change the Assembly name to have .net on the end of it. The resulting file will end with .net.dll Which is the correct naming for a .net c# script. Clicking on Assembly Information, we can change the title, description and set the version number of the script.

http://www.shadowsre...ts/dotnet/8.jpg

Right now we've set them. I think we're ready to compile and test our script.

QUOTE

VS2008 - press F7 to build, in the output window it should say:
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========


If it has an error, and the error box isn't showing. Go to View -> Other Windows -> Error List

QUOTE

C#E2005 - Press F6 to build, in the output box this should be shown.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========


Deal with any errors, and when it compiles sucessfully. Copy the .net.dll file from the build folder. Default is the project folder debug\bin. To your gta folder /scripts. Now when you run the game, your health and amour should be set every 20 seconds, and when you're in a vehicle it should be repaired / washed every 20 seconds. And pressing Q should spawn an an helicopter.

Congratulations you've just sucessfully made your first script for the .net scripthook. You can stop reading here and go play around with it some more. Or you can continue, and I'll explain some more features that can be put into scripts such as catching called mobile numbers, console commands etc.

Right lets move on, you've got a pretty simple script that heals the player every 20 secs, and allows the player to spawn a helicopter. It's all well and good having a set Interval, but if we want to change it we have to recompile the script everytime. How about we have a ini file that has the time for us? That we can then access and set the time from that.

Luckily the scripthook, has a way of reading from a ini file. And already has a script settings propery. Lets start with making the ini file. The ini file must have the filename as the script. So if you're scripts called tutorialScripVS.net.dll then the ini must have the name tutorialScriptVS.net.ini

CODE

[SETTINGS]
INTERVAL = 10000
LOADFUEL = Control, W


Thats all that you need, the Key INTERVAL is in the SETTINGS catergory. Keeping to this format keeps the ini file easy to read when it gets cluttered. Now to access this in the scripthook. We use the Settings property. Just replace the line: Interval = 20000; with

CODE

Interval = Settings.GetValueInteger("INTERVAL", "SETTINGS", 10000);


Where the 10000 is the default value if it cannot load from the ini file. So now we've got a script that reads from an ini file. So we no longer have to recompile if we wish to change the time. All we have to do is change the time in the ini file, save it. Then use the console to reload the scripts.

We can also use the ini file to set the Key to look for. Using:

CODE

Settings.GetValueKey("KEYPRESS", "SETTINGS", Keys.Q);


Will allow us to load in a key type to check against. We need to create a Key varible above the constructor, to hold our keyPress key.

CODE

Keys keyP;


Then in the constructor we can set keyP to the settings value.

CODE

KeyP = Settings.GetValueKey("KEYPRESS", "SETTINGS", Keys.Q);


So now we can control the keyPress and the Interval time from the ini file. But we cannot save to the ini file. That is coming in a later version of the scripthook. Lets move on to catching mobile numbers. The phone feature in IV is pretty cool, but using the scripthook we can catch the numbers dailed. So we could dail "HEL 555 0100", and spawn a FIB car.

To do this, we have to bind the number to a method that is called when the number is dailed. We can use the BindPhoneNumber function. We need to do this in the constructor, as we only need to bind the number once.

CODE

BindPhoneNumber("FIB 555 0100", new PhoneDialDelegate(callHandle));


Now we can create a method, add some spawn code to it.

CODE

public void callHandle()
{
//get position on street near player
Vector3 vehPos = World.GetNextPositionOnStreet(Player.Character.Position.Around(10.0f));
//create vehicle
World.CreateVehicle(new Model("FIB"), vehPos);
}


You'll notice I've used a method called GetNextPositionOnStreet. This method will get a position, so the vehicle is positioned on the street correctly. Now when you dial FIB 555 0100 on the phone, you'll get a FIB car. But you won't know its been spawned until you see it. Lets add a simple message that says "Vehicle Spawned" In the callHandle method add the line:

CODE

Game.DisplayText("Vehicle Spawned");


Your code at this point should look something like this:

CODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA;

namespace tutorialScriptVS
{
   public class tutScriptVS : Script
   {

       public tutScriptVS()
       {
           //set interval
           Interval = Settings.GetValueInteger("INTERVAL", "SETTINGS", 10000);

           //bind tick event
           this.Tick += new EventHandler(testTick);

           //bind keydown event.
           this.KeyDown += new GTA.KeyEventHandler(testKeyDown);

           //bind phone
           BindPhoneNumber("FIB 555 0100", new PhoneDialDelegate(callHandle));

       }

           //tick method, ran every 20 secs
           public void testTick(object sender, EventArgs e)
           {
               //set health and amour
               Player.Character.Health = 1000;
               Player.Character.Armor= 1000;

               if (Player.Character.isInVehicle())
               {
                   //repair
                   Player.Character.CurrentVehicle.Repair();
                   //wash
                   Player.Character.CurrentVehicle.Wash();
                   
               }

           }

           //key down handler
           public void testKeyDown(object sender, GTA.KeyEventArgs e)
           {
               if (Keys.Q == e.Key)
               {
                   //get position to put vehicle
                   Vector3 vehPos = Player.Character.Position.Around(10.0f);
                   
                   //create vehicle
                   World.CreateVehicle(new Model("ANNIHILATOR"), vehPos);
               }
           }

           public void callHandle()
           {
               //get position on street near player
               Vector3 vehPos = World.GetNextPositionOnStreet(Player.Character.Position.Around

(10.0f));
               //create vehicle
               World.CreateVehicle(new Model("FIB"), vehPos);
           }
       
   }
}


Its looking good so far, we're reading from an ini file to set an interval and the key press. And we can now phone a number to get a FIB car. We can also do the same and spawn a police by using the console. We can bind a command for the console. Using the BindConsoleCommand method. Again in the constructor we need to put:

CODE

BindConsoleMethod("spawn polcar", new ConsoleCommandDelegate(consoleHandle));


And we can then add in the method and spawn code.

CODE

public void consoleHandle()
           {
               //get position on street near player
               Vector3 vehPos = World.GetNextPositionOnStreet(Player.Character.Position.Around(10.0f));
               //create vehicle
               World.CreateVehicle(new Model("POLICE"), vehPos);
           }



And the finished code.

CODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA;

namespace tutorialScriptVS
{
   public class tutScriptVS : Script
   {

       public tutScriptVS()
       {
           //set interval
           Interval = Settings.GetValueInteger("INTERVAL", "SETTINGS", 10000);

           //bind tick event
           this.Tick += new EventHandler(testTick);

           //bind keydown event.
           this.KeyDown += new GTA.KeyEventHandler(testKeyDown);

           //bind phone
           BindPhoneNumber("FIB 555 0100", new PhoneDialDelegate(callHandle));

       }

           //tick method, ran every 20 secs
           public void testTick(object sender, EventArgs e)
           {
               //set health and amour
               Player.Character.Health = 1000;
               Player.Character.Armor= 1000;

               if (Player.Character.isInVehicle())
               {
                   //repair
                   Player.Character.CurrentVehicle.Repair();
                   //wash
                   Player.Character.CurrentVehicle.Wash();
                   
               }

           }

           //key down handler
           public void testKeyDown(object sender, GTA.KeyEventArgs e)
           {
               if (Keys.Q == e.Key)
               {
                   //get position to put vehicle
                   Vector3 vehPos = Player.Character.Position.Around(10.0f);
                   
                   //create vehicle
                   World.CreateVehicle(new Model("ANNIHILATOR"), vehPos);
               }
           }

           public void callHandle()
           {
               //get position on street near player
               Vector3 vehPos = World.GetNextPositionOnStreet(Player.Character.Position.Around(10.0f));
               //create vehicle
               World.CreateVehicle(new Model("FIB"), vehPos);
           }

           public void consoleHandle()
           {
               //get position on street near player
               Vector3 vehPos = World.GetNextPositionOnStreet(Player.Character.Position.Around(10.0f));
               //create vehicle
               World.CreateVehicle(new Model("POLICE"), vehPos);
           }
       
   }
}


And thats all for time being, I'll try and keep it update to date, and hopefully add some more guides to it. I hope this helps anyone who wishes to start writing .net scripts for IV. If there are any errors within this, please let me know.

My advice for anyone writing scripts, it just to play around with the properties and methods avaible to you. Also use the Class view option, its very useful for discovering what a reference can do.
  • jpm1, SaucyPow3r, Sweet Bellic and 1 other like this

boomer678
  • boomer678

    Mark Chump

  • Members
  • Joined: 02 Jan 2009

#2

Posted 06 March 2009 - 10:23 PM

Great cookie.gif cookie.gif cookie.gif cookie.gif cookie.gif cookie.gif cookie.gif

CaptainDingo
  • CaptainDingo

    Not actually a captain, or a dingo.

  • Members
  • Joined: 18 Jan 2009

#3

Posted 07 March 2009 - 12:08 AM

I appreciate all the time you took to do this, but unfortunately this is worthless for people like me who don't know C# already. confused.gif

It's probably hard for someone who already knows it all, but from my perspective, this is nearly impossible. I can read it five times over and still not understand it, at all. I mean, I understand variables, strings, variable and string operations, but I don't understand the structure encased around them one bit.

It's a shame because I have a logical mind for programming, but nobody has come along yet who is really capable of teaching me how to actually program successfully. Like what's a method? What's "public class tutScriptVS : Script" do? What is "(object sender, GTA.KeyEventsArgs e)" all this stuff for? What does any of it mean? It looks like gibberish to me. Arg is the sound I make when I take a huge crap, and that's all I know. notify.gif

It's all the little things like this that coders take for granted when they're trying to teach and gloss over or ignore them, but that seriously make me feel confused and unable to learn any of this.

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#4

Posted 07 March 2009 - 12:49 AM

QUOTE (CaptainDingo @ Mar 7 2009, 01:08)
I appreciate all the time you took to do this, but unfortunately this is worthless for people like me who don't know C# already. confused.gif

It's probably hard for someone who already knows it all, but from my perspective, this is nearly impossible. I can read it five times over and still not understand it, at all. I mean, I understand variables, strings, variable and string operations, but I don't understand the structure encased around them one bit.

It's a shame because I have a logical mind for programming, but nobody has come along yet who is really capable of teaching me how to actually program successfully. Like what's a method? What's "public class tutScriptVS : Script" do? What is "(object sender, GTA.KeyEventsArgs e)" all this stuff for? What does any of it mean? It looks like gibberish to me. Arg is the sound I make when I take a huge crap, and that's all I know. notify.gif

It's all the little things like this that coders take for granted when they're trying to teach and gloss over or ignore them, but that seriously make me feel confused and unable to learn any of this.

In that case I'll add a few extra bits in, in the coming days but for now

what's a method?
- A method is basically a block of code, that can be reused.

What's "public class tutScriptVS : Script" do?
- Basically it defines a class, something that can be reused again and again. That code above, it defining a class that is public(accessible to all) and with a name of tutScriptVS, and inherits all the properties and methods from the Script class.

What is "(object sender, GTA.KeyEventsArgs e)
- Its a method call, where you're passing in a datatype of object called sender, and a KeyEventsArg called e. I'm assuming the sender object, is the window or method that called this method. And the KeyEventsArg is basically a class, and within that class will be methods/properties relating to Key Events in GTA.

Any other questions and I'll try my best to answer them.

CaptainDingo
  • CaptainDingo

    Not actually a captain, or a dingo.

  • Members
  • Joined: 18 Jan 2009

#5

Posted 07 March 2009 - 01:40 AM Edited by CaptainDingo, 07 March 2009 - 03:23 AM.

Thanks.

Also, how did you find out things like Player.Character.Health, Player.Character.Current.Vehicle, and um... just other in-game stuff like that? Is there some list of these game references we can use so we can know what they all are for altering?

So like, how can I make it so something happens when the player dies?

Edit: Nevermind, figured most of it out.

However, it seems I can't find out how something happens if Niko is busted rather than dead. There's a player.character.isdead but there's no player.character.busted/arrested or equivalent.

coin-god
  • coin-god

    High Roller

  • $outh $ide Hoodz
  • Joined: 18 Mar 2007
  • None

#6

Posted 07 March 2009 - 03:38 AM

Awesome tutorial, piece og gold. I wilñ study it when i get Visual Studio.

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#7

Posted 07 March 2009 - 09:59 AM

QUOTE (CaptainDingo @ Mar 7 2009, 02:40)
Thanks.

Also, how did you find out things like Player.Character.Health, Player.Character.Current.Vehicle, and um... just other in-game stuff like that? Is there some list of these game references we can use so we can know what they all are for altering?

So like, how can I make it so something happens when the player dies?

Edit: Nevermind, figured most of it out.

However, it seems I can't find out how something happens if Niko is busted rather than dead. There's a player.character.isdead but there's no player.character.busted/arrested or equivalent.

You just have to play around with the scripthook. Using the class reference view of Visual studio, you can see what properties and methods the scripthook has.

Some things aren't put into the scripthook yet, so at the moment there is no way of dertermining if they are arrested.

You can however use a native call, if you can find the native needed and work out its parameters. http://www.gtamoddin...ative_functions Has a list of native functions. And there is one called IS_PLAYER_BEING_ARRESTED

Now I don't know the parameters for this, but all I assume is it will use one bool value to send the result to. To use a native you can use the GTA.Native.Function.Call("NATIVE_FUNCTION_HERE", params) method.

CODE

bool arr = false;
GTA.Native.Function.Call("IS_PLAYER_BEING_ARRESTED", arr);


Now I've no idea if the above works, you've just got to play around with the parameters or search, although nothing came up for that one.

CaptainDingo
  • CaptainDingo

    Not actually a captain, or a dingo.

  • Members
  • Joined: 18 Jan 2009

#8

Posted 08 March 2009 - 11:38 AM

Hm. You noted that we can't save to a .ini file, but in your Petrol Mod, you do save the last car's fuel value after leaving the game. How'd you do that?

the hubster
  • the hubster

    Sup Homies

  • Members
  • Joined: 03 May 2005

#9

Posted 08 March 2009 - 11:54 AM Edited by the hubster, 08 March 2009 - 12:15 PM.

Seeing as this tutorial looks like it is for those new to Visual Studio, I think it is worth mentioning the object browser. You wont be able to get very far in writing scripts if you do not use the object browser to find all the classes/properties/functions you need relating to GTA. Apart from that, nice tutorial, I'm sure allot of people will find it very useful. (Image)

QUOTE
Hm. You noted that we can't save to a .ini file, but in your Petrol Mod, you do save the last car's fuel value after leaving the game. How'd you do that?

He uses the windows function WritePrivateProfileString

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#10

Posted 09 March 2009 - 09:16 AM

I think I refered to the object browser as the class refecence thingy, oops. Yeah I've got a function written that uses the WritePrivateProfileString.

CoMPMStR
  • CoMPMStR

    CoMPuTer MAsSteR

  • Members
  • Joined: 18 Dec 2008

#11

Posted 09 March 2009 - 05:45 PM

QUOTE (CaptainDingo @ Mar 6 2009, 19:40)
However, it seems I can't find out how something happens if Niko is busted rather than dead. There's a player.character.isdead but there's no player.character.busted/arrested or equivalent.

Another way you could check if the player got busted, is by using the GetIntegerStatistic method, very easy and I know for a fact it works (in SP that is, I doubt it works in MP).

CODE
   Private timesBusted As Integer = Game.GetIntegerStatistic(IntegerStatistic.TIMES_BUSTED)

   Private Sub ssp_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Tick
       If Game.GetIntegerStatistic(IntegerStatistic.TIMES_BUSTED) > timesBusted Then
           timesBusted = Game.GetIntegerStatistic(IntegerStatistic.TIMES_BUSTED)
           ' do the busted code here
       End If
   End Sub


Of course, this example is in VB but can easily be converted to C# from developerfusion.com. As with all other automatic conversion tools, it's far from perfect but it works for the most part. Just remove Handles Me.Tick, otherwise it will throw an error.
http://www.developer...t/vb-to-csharp/
http://www.developer...t/csharp-to-vb/

Beaverman
  • Beaverman

    Player Hater

  • Members
  • Joined: 06 Mar 2009

#12

Posted 11 March 2009 - 08:21 PM

I can't relly get it to work. i get errors in:

CODE
           
[COLOR=red]this.Tick[/COLOR] += new EventHandler(testTick);
[COLOR=red]this.KeyDown [/COLOR]+= new KeyEventHandler(keyDown);


my complete code is:

CODE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA;

namespace Test_script
{
   public class Test : Script
   {
       public Test()
       {
           this.Interval = 100;
           this.Tick += new EventHandler(testTick);
           this.KeyDown += new KeyEventHandler(keyDown);
       }

       public void testTick (object sender, EventArgs e)
       {
           if (Player.Character.isDead)
           {
               Game.DisplayText("Wasted");
           }
       }

       public void keyDown (object sender, KeyEventArgs e)
       {
           if (Player.Character.isInVehicle())
           {
               Game.DisplayText("Drive");
           }
           else
           {
               Game.DisplayText("RUN");
           }
       }
   }
}


i can't compile due to this. confused.gif

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#13

Posted 11 March 2009 - 08:27 PM

What errors are you getting exactly?

Beaverman
  • Beaverman

    Player Hater

  • Members
  • Joined: 06 Mar 2009

#14

Posted 11 March 2009 - 09:13 PM

Cannot assign to 'Tick' because it is a 'method group'

Cannot assign to 'KeyDown' because it is a 'method group'

bored.gif

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#15

Posted 11 March 2009 - 09:20 PM

Thats strange, never had that error before.

What are you using to compile it in? Is all the references set up correctly? And what version of the scripthook are you using?

Beaverman
  • Beaverman

    Player Hater

  • Members
  • Joined: 06 Mar 2009

#16

Posted 11 March 2009 - 09:45 PM

ohh it works now. probobly an outdated version of -net scripthook tounge.gif

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#17

Posted 11 March 2009 - 10:47 PM

QUOTE (Beaverman @ Mar 11 2009, 22:45)
ohh it works now. probobly an outdated version of -net scripthook tounge.gif

Yep thats ya cause, outdated version. 0.86 changed the way everything worked. Glad you got it sorted.

Beaverman
  • Beaverman

    Player Hater

  • Members
  • Joined: 06 Mar 2009

#18

Posted 12 March 2009 - 09:17 AM

only one plroblem remain. when i push a button. i should just type run, but the game chrashes the code is the same. what have i done wrong sarcasm.gif

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#19

Posted 12 March 2009 - 03:15 PM

Is there an error message in the ScriptHookDotNet.log file?

Threepwood
  • Threepwood

    Calm like a bomb.

  • Members
  • Joined: 13 Jul 2008

#20

Posted 13 March 2010 - 07:14 AM

Is there someone who could please give me a short, clean example of a script which just gets the speed of the car you sit in and displays it (no special things, just like upper left corner digits, no eye candy).
I cant find any scripts to peep into for learning purposes, so if someone is kind enough.. thanks a lot. Basically I just need a full frame and getting a keypress + working out a handler to get the speed and compute it, so I can see how it should be done and work with it. That tons of .dll/.asi Speedos arent any help for this.

Andrew
  • Andrew

  • Andolini Mafia Family
  • Joined: 21 Jul 2003
  • None

#21

Posted 13 March 2010 - 09:40 AM

If you use the above tutorial, which shows you how to use key presses and display stuff on the screen, that's the starting framework, then all you need to do is grab the current vehicle, and grab the speed from it.

CODE

//check if we are actually in a vehicle
if(Player.Character.isInVehicle())
{
float speed = Player.Character.CurrentVehicle.Velocity;
}


or something along those lines.

Threepwood
  • Threepwood

    Calm like a bomb.

  • Members
  • Joined: 13 Jul 2008

#22

Posted 13 March 2010 - 12:05 PM

Yeah figured it out step by step and with some other help - its not easy to jump into smth completely new wink.gif
Thanks for that!

Gokaic
  • Gokaic

    Rat

  • Members
  • Joined: 17 Jan 2006

#23

Posted 15 May 2010 - 07:06 AM Edited by Gokaic, 15 May 2010 - 07:09 AM.

I was going to ask how this was done, but I decided to do a bit of research and thought I'd post my findings. Please correct me if I'm wrong, because I haven't had a chance to test this.

This is how you would set information to a .ini file. I'm using the ini file format from the original tutorial for continuity:
CODE

Settings.SetValue("INTERVAL", "SETTINGS", [VALUE]);


where [VALUE] is the value you are trying to set. So let's say you have an integer with a set amount, and you want to write this amount into your ini file.

CODE

int i = 10000;
Settings.SetValue("INTERVAL", "SETTINGS", i);



EDIT: Sorry to dig up a few month old post, but I thought if I can help some green scripters learn how to make the next greatest mod, it would be worth it, and it would also be nice to continue this tutorial so we can all learn how to create better scripts.

 dice
  •  dice

    Always rolling

  • The Yardies
  • Joined: 12 Aug 2008
  • None
  • Best Server SAMP 2009

#24

Posted 24 January 2011 - 03:39 PM Edited by ^- Dice ->, 24 January 2011 - 03:41 PM.

Pardon my ignorance, but couldn't you just write your own system using System.Windows.IO ?

Edit: Sorry for the bump, didn't notice the post date

samestreet
  • samestreet

    Player Hater

  • Members
  • Joined: 05 Aug 2011

#25

Posted 24 November 2011 - 05:38 AM

Hey friend! Do you know how to spawn attacking peds and, also, bodyguards? I was trying to do it through the Ped.Tasks (building a sequence) but it is not working. I'm also trying to detect when some mobs are dead, but, it seems to be not working properly. Something like this:

Ped ped;
ped = World.CreatePed(Player.Character.Position.Around(5.0f));

// code code code

if(ped.isDead)
// code code code

jitsuin
  • jitsuin

    Meat Popsicle

  • BUSTED!
  • Joined: 06 Dec 2011

#26

Posted 11 December 2011 - 07:30 AM Edited by jitsuin, 14 January 2012 - 08:50 PM.

dumb question deleted blush.gif

YossiBz
  • YossiBz

    Square Civilian

  • Members
  • Joined: 13 Jun 2010

#27

Posted 25 January 2012 - 05:40 PM

how I flip a car?

jitsuin
  • jitsuin

    Meat Popsicle

  • BUSTED!
  • Joined: 06 Dec 2011

#28

Posted 26 January 2012 - 06:12 PM Edited by jitsuin, 26 January 2012 - 06:24 PM.

why does this not work when i try BindPhoneNumber? I place this.BindPhoneNumber in script constructor and create my method below the constructor

CODE
using System;
using System.Windows.Forms;
using GTA;

namespace GTA
{
   public class GTA : Script
   {
       public GTA()
       {
           this.BindPhoneNumber("1234567890", new PhoneDialDelegate(WrongNumber));
       }
       private void WrongNumber()
       {
           Game.DisplayText("Wrong Number Bitch");
       }
   }
}


i dial 1234567890 on phone but no method is called and i just get a busy signal, no text is displayed...thanks!!!

i also tried multiple strings for phone number like "777 400" or "1111" or "1111111111" and nothing confused.gif

gatienthe
  • gatienthe

  • Members
  • Joined: 28 Jan 2012
  • None

#29

Posted 28 January 2012 - 03:46 PM

Hi,

Thank you for the tutorial !

jitsuin, i think you need to do that
CODE
BindConsoleCommand("WrongNumber", new ConsoleCommandDelegate(WrongNumberCommand), "Wrong Number Bitch");

Don Wasyl
  • Don Wasyl

    Player Hater

  • Members
  • Joined: 22 Jun 2007

#30

Posted 09 May 2012 - 11:34 PM

Andrew, could you reupload images in your tutorial?
Tranks!




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users