Please or Register to create posts and topics.

Vscripting/coding tutorial? 🙁

Hi experts.

I wanted to ask a question directed to the people who write Vscripts for their maps or other people's maps. Basically, where did you learn Vscripting? If I did my research right, Valve's Vscripts are written in the "Squirrel" programming language, so I went looking for tutorials on how to read it. Unfortunately, every tutorial I've found seems to rely on the idea that the reader already knows the basics of programming. The readings were filled with things like "similar to Luna" and "much like C++". I then thought, "ok, maybe C++ is what I need?" but I've heard so much things about how hard it is.

No matter how hard I try, it's like one program leads to another and...

*sigh*

I just need somewhere to start. Where can I learn the very basics of programming in general? Or if each language is different, which should I learn in order to write working Vscripts?

Favorite theme: Retro Aperture

Now let's solve this thing! ^^

Well, I'm not an expert at all, but I'm also interested in scripting lately... There's no Portal2 scripting tutorials (yet). I don't know what you're managing to get but I'd suggest you to begin for learning how some scripts work, then from that, I'm sure you can deduct many things about how they work even though you don't understand programming language...

For example, have you ever made your own custom transition.nut script for making 2 of your maps be played in a row? Do it. One of the many good articles in the VDC here

Have you tried to implement your own music into any of your maps? do it. Check this tutorial

Also go to the VDC and check Portal2 script funtions (this will allow you to know what could be really done by scripting)

I guess this is a point to start.. isn't it?

ImageImageImageImageImageuseful tools and stuff here on TWP :thumbup:
[spoiler]ImageImageImageImageImage[/spoiler]

Thanks for that, I read all of them and I think I know how to make a basic script. But see, I want to make a whole new testing element. I'm gonna just tell you guys what I had in mind just so you know. I want to make like a little rotating sun that shoots a straight beam of light that can travel through portals, bounce off mirrors, and activate various objects (like a solar powered panel or a "light catcher" rather than a laser catcher). Doing a Google search gave me something similar to what I want it to look like: http://thumbs.dreamstime.com/thumblarge ... KPxE87.jpg . For now, I was thinking of making a simple box that shoots the beam just to test it. The problem is: I don't know how to make the beam in the first place. I looked at the Vscript for Sendificate as suggested and I can only make out vague things that it might be doing in that script. I'm not sure how Sendificate will help me make this since I can't read it. Sorry if I sound like I'm complaining, I'm not at all :D. I'm just a little frustrated because I want to make this idea and I can't figure it out. Can someone give me help somehow? :)

Favorite theme: Retro Aperture

Now let's solve this thing! ^^

What you're trying to do sounds outrageously complex and over-the-top. Generally if you don't know the basics on how to code, then coding will be relatively difficult. The script you are trying to understand is very advanced.
Even though it is nicely commented and says what does what, it still a little difficult to follow.

If you havent done so, play HMW's maps Sendificate so you know what the script is for.

EDIT: In order to get lasers going through portals, he literally traced the laser through all possible previous cubes and portals in order to find out where the laser will start again. Lasers can go through portals, however env_beams cannot, and he did use one as an effect. (Correct me if I am wrong) but he found the origin of a portal, found the other one and used a length of a vector in order to decide where the next point of the beam will go. For this, complex mathematics I have trouble understanding were used.

I can help you understand some basics here if you want:
His code

Spoiler
Please note:
It is not necessary to have a ; (semicolon) after each line of code in squirrel, however it is in other languages such as Java or C++.

Everything after

Code: Select all
//________________________________________________________________________
//                            Static symbols
//________________________________________________________________________

and before

Code: Select all
//  ________________________________________________________________________
//                         Angle and vector math
//  ________________________________________________________________________

are constants. i.e. Named values that can be referenced in any function through the script.
If you find something like: ::sendtor_platform that is not declared inside the global variables or inside a function (prefixed by local), it is because it is created through RunScriptCode inside hammer (look inside the laser emitter instances and cube teleporters).

Code: Select all
destination_fx <- Entities.FindByName(null, "@sendtor_frame_cube_e");

Is finding an entity inside the map and putting it inside a variable for easy access. This is mighty useful as the entity that it references is easily referenced through the code.

Entities.FindByName is a Source-specific code. This is the same for EntFire, EntFireByHandle, Entities.FindByClassname, the .getOrigin() .getAngles(), .getABSOrigin etc.

Code: Select all
function vector_length(v)
{
    // Return the length of a vector.
    return sqrt(pow(v.x, 2) + pow(v.y, 2) + pow(v.z, 2));
}

This is a function. Functions are snippets of code that do certain things which can be called multiple times in other functions or straight through hammer using the RunScriptCode command. In this case he has nicely commented the code so we know exactly what it does.

The sqrt() is another way of saying 'squareroot' the value which is calculated inside these brackets. pow(x, y) is another way of saying we want x to the power of y. Vector is a line inside the world that has direction and length but no physical points. For example a vector would be a direction and/or magnitude of x,y,z.
So literally all this function is, is using a math formula to find the distance of a vector which is passed to the function (v). Since (this) vector has 3 values inside of it, the x value can be grabbed through vector.x (in in his v.x) etc.

To call this function to calculate a vectors distance you will need to first get or make a vector by initializing it and then using the function vector_length(vectorname) it will return the length. In order to make this length usable, you will need to either put it in a statement which you will use for some sort of logic or store it in a variable.
e.g1. someFunctionThatUsesAVector(vector_length(Vector(x, y, z)))
e.g2. vectorLength = vector_length(Vector(x, y, z))

Of course the reason why he suggested using it was because he has already done most of the work for you, just understanding what snippets to use is the problem. Try asking him to strip out the unnecessary bits because this honestly looks like a lot of work.

?????????????????????????????TWP Releases | My Workshop

I see Chicken already made a start explaining some of the basic scripting things, which is very nice!
I'll try to expand on that a bit:

Lots of elementary scripting stuff

Spoiler
Comments
Every line starting with // is completely ignored. It is used to add comments and explanation to a script, so other people can make sense of it. It also helps when you've written a piece of code and you come back to it a few weeks later thinking "What the hell was this for again?" :)

Statements
A statement is one thing you ask the script to do, for example "add A to B" or "move this entity here". In many programming languages (most notably C and Java), statements are separated by semicolons. Squirrel is smart enough to figure out that if a particular line makes sense on its own (no unpaired parentheses and stuff like that), it must be a single statement. I like to add semicolons anyway because I'm used to doing that, but you don't have to.

Variables
A variable is anything that your script needs to remember for any length of time. There are "static" variables that just sit there and keep their value until you change them. Anywhere you see "something <- something_else", that's a static variable being made, and it is attached to the entity that is running that code. Any other code that this entity runs, can read or modify this variable at any time after it is created. There's a whole bunch of them near the top of the script, but they can be created from anywhere in the code, at any time. (Even inside functions.) Of course, you have to be careful that you don't try to read a static variable before it is created!

(As Chicken pointed out, a lot of these are used as constants. For example, I assigned some entities to static variables (platform_fx and destination_fx for example) so I don't have to look them up every time I need them. Programmers love convenience!)

There are also "local" variables, which are used in a specific section of code and disappear as soon as that code is done running. They are specified with "local my_variable = something". You see these a lot inside functions.

Lastly, there are "global" variables. These work like static variables, but they are prefixed with two semicolons. These are not specific to one entity: they can be read and changed by ANY entity in the map.

::sendtor_source_offset is an example of this. There are also variables that are set by other entities via RunScriptCode, like ::sendtor_source and ::sendtor_platform. This is why you don't see them get an initial value in the script. (I have done my best to document all of these though.)

Functions
Chicken already explained these quite well. Saves me some typing!

Arrays
Normally, a variable will hold exactly one thing. What if you want to keep track of a group of things? You put them in an array, like this:

my_array <- [thing, another_thing, that_thing_has_numbers_on_it]

You can also start with empty ones...

lasers <- []
targets <- []
sprites <- []

... and fill them up as you go:

lasers.append(laser)
targets.append(target)
sprites.append(sprite)

To get a thing out of an array: "lasers[0]", or "lasers[index]" where "index" is a variable holding a number.

Setting things in motion
Now, after you have made a script, how do you make the game use it? There are a couple of ways:

  1. Every entity has an "entity scripts" attribute, where you fill in the name of a script file in the "vscripts" folder. The entity will run the script when the map loads. After doing that, it will have the capability to use any variables or functions that were defined in that script file, which can be called by the methods described below. If you put any code (besides variable declarations) outside any functions, it will also run when the map loads. While this may be useful in some cases, I don't recommend doing that because it's hard to control, and after it runs, it's gone. If instead you define a function, you can call that any time and as often as you like.
  2. RunScriptCode. This input (again, common to all entities) will run any script code supplied in the input parameter. To run a function in a script, send the entity a RunScriptCode input with something like "my_function()". The parentheses are required, even if the function itself doesn't take any parameters. You can put any valid script code in there (like setting a static variable with "my_var <- value") except one limitation: NO TEXT STRINGS! As you can see elsewhere in the code, text strings are delimited by double quotes. The same double quotes are also used in the map file to delimit data values. Inserting double quotes in any input box in Hammer will make a royal mess of your map file, so do not ever do that!
  3. You can also put a function name in the "script think function" entity attribute, and it will automatically be called every 0.1 seconds. (Note: a function name, without the parens this time. NOT a statement like "variable <- value".)
  4. And lastly, there is the "RunScriptFile" input. When an entity receives this input, it runs the script file given in the parameter.

The logic_script entity
This is an entity especially suited to running scripts. Besides the normal scripting facilities described above, it has a special static variable called EntityGroup. This is an array that is filled with the entities you add to the logic_script in Hammer, so you don't have to go look for them with Entities.FindByName(...).
(The entity that runs the sendificator.nut script is a logic _script. It is located in the sendtor_manager template.)


So. I know that's a lot of info to take in, but I hope it helps. Definitely take your time while reading all that, and maybe you can have a look through some of Valve's scripts in the vscripts folder and try to understand them.

I was planning to add a more elaborate explanation about how the script itself works, but it's getting late right now so that will have to wait. I will come back on this later!

Sendificate series: Sendificate | A Beam Too Far | Airtime | 302
Other Portal 2 maps: Medusa Glare
Portal 1 maps: Try Anything Twice | Manic Mechanic

Oh wow, you guys both helped a ton. I read through both HMW's and ChickenMobile's posts and I am able to understand the basic stuff (most easy to understand for me is the "<-" part and comments) although the other stuff is much more advanced for a beginner like me. I've been toying around in Hammer with my idea, and it looks like I'm going to be able to make some basic parts of my idea with just Inputs/Outputs.

For example, I have a beam_spotlight shining onto a white wall that allows one portal(WallA). There is also another white wall that allows one portal(WallB). I then put a func_portaldetector on each, created a math counter, and added another beam_spotlight in front of WallB that starts off. When a portal is placed on either WallA or B, the counter adds one if detecting a linked portal or subtracts one if the portal stops touching it. Then, when both walls have a linked portal on them, the beam_spotlight in front of WallB will turn on, giving the appearance that it is traveling through the portal.

Through simple I/O things like that, I think I'll figure out how to do this. I'm better at manipulating I/Os to do stuff in the map than scripting. I AM going to save this information, though. It'll definitely help me if I ever want to try scripting. A big Thank You to both of you! :D

Favorite theme: Retro Aperture

Now let's solve this thing! ^^

Thanks Chickenmobile and HMW for this awesome introduction to scripting! It's very handy for newbies like us with ideas to develop! :thumbup:

ImageImageImageImageImageuseful tools and stuff here on TWP :thumbup:
[spoiler]ImageImageImageImageImage[/spoiler]
HMW wrote:
So. I know that's a lot of info to take in, but I hope it helps. Definitely take your time while reading all that, and maybe you can have a look through some of Valve's scripts in the vscripts folder and try to understand them.

Decompiling (as in understanding) one of Valve's scripts is the way to go. Seeing as many people would have worked on these scripts, it probably has been put in the most generalised way for everyone working on it to understand.

Of course the main difference between us and Valve is they have access to the game code and we don't, meaning new elements would be put into the game instead of using scripts to do the same thing.

If you want to learn general programming rules etc. Go look at tutorials on learning Java or C++. They are way more documented than squirrel and are used for teaching devices. (I learned Java at school and my brother learned C++ at his tertiary education).

?????????????????????????????TWP Releases | My Workshop
HMW wrote:
I see Chicken already made a start explaining some of the basic scripting things, which is very nice!
I'll try to expand on that a bit:

Lots of elementary scripting stuff

Spoiler
Comments
Every line starting with // is completely ignored. It is used to add comments and explanation to a script, so other people can make sense of it. It also helps when you've written a piece of code and you come back to it a few weeks later thinking "What the hell was this for again?" :)

Statements
A statement is one thing you ask the script to do, for example "add A to B" or "move this entity here". In many programming languages (most notably C and Java), statements are separated by semicolons. Squirrel is smart enough to figure out that if a particular line makes sense on its own (no unpaired parentheses and stuff like that), it must be a single statement. I like to add semicolons anyway because I'm used to doing that, but you don't have to.

Variables
A variable is anything that your script needs to remember for any length of time. There are "static" variables that just sit there and keep their value until you change them. Anywhere you see "something <- something_else", that's a static variable being made, and it is attached to the entity that is running that code. Any other code that this entity runs, can read or modify this variable at any time after it is created. There's a whole bunch of them near the top of the script, but they can be created from anywhere in the code, at any time. (Even inside functions.) Of course, you have to be careful that you don't try to read a static variable before it is created!

(As Chicken pointed out, a lot of these are used as constants. For example, I assigned some entities to static variables (platform_fx and destination_fx for example) so I don't have to look them up every time I need them. Programmers love convenience!)

There are also "local" variables, which are used in a specific section of code and disappear as soon as that code is done running. They are specified with "local my_variable = something". You see these a lot inside functions.

Lastly, there are "global" variables. These work like static variables, but they are prefixed with two semicolons. These are not specific to one entity: they can be read and changed by ANY entity in the map.

::sendtor_source_offset is an example of this. There are also variables that are set by other entities via RunScriptCode, like ::sendtor_source and ::sendtor_platform. This is why you don't see them get an initial value in the script. (I have done my best to document all of these though.)

Functions
Chicken already explained these quite well. Saves me some typing!

Arrays
Normally, a variable will hold exactly one thing. What if you want to keep track of a group of things? You put them in an array, like this:

my_array <- [thing, another_thing, that_thing_has_numbers_on_it]

You can also start with empty ones...

lasers <- []
targets <- []
sprites <- []

... and fill them up as you go:

lasers.append(laser)
targets.append(target)
sprites.append(sprite)

To get a thing out of an array: "lasers[0]", or "lasers[index]" where "index" is a variable holding a number.

Setting things in motion
Now, after you have made a script, how do you make the game use it? There are a couple of ways:

  1. Every entity has an "entity scripts" attribute, where you fill in the name of a script file in the "vscripts" folder. The entity will run the script when the map loads. After doing that, it will have the capability to use any variables or functions that were defined in that script file, which can be called by the methods described below. If you put any code (besides variable declarations) outside any functions, it will also run when the map loads. While this may be useful in some cases, I don't recommend doing that because it's hard to control, and after it runs, it's gone. If instead you define a function, you can call that any time and as often as you like.
  2. RunScriptCode. This input (again, common to all entities) will run any script code supplied in the input parameter. To run a function in a script, send the entity a RunScriptCode input with something like "my_function()". The parentheses are required, even if the function itself doesn't take any parameters. You can put any valid script code in there (like setting a static variable with "my_var <- value") except one limitation: NO TEXT STRINGS! As you can see elsewhere in the code, text strings are delimited by double quotes. The same double quotes are also used in the map file to delimit data values. Inserting double quotes in any input box in Hammer will make a royal mess of your map file, so do not ever do that!
  3. You can also put a function name in the "script think function" entity attribute, and it will automatically be called every 0.1 seconds. (Note: a function name, without the parens this time. NOT a statement like "variable <- value".)
  4. And lastly, there is the "RunScriptFile" input. When an entity receives this input, it runs the script file given in the parameter.

The logic_script entity
This is an entity especially suited to running scripts. Besides the normal scripting facilities described above, it has a special static variable called EntityGroup. This is an array that is filled with the entities you add to the logic_script in Hammer, so you don't have to go look for them with Entities.FindByName(...).
(The entity that runs the sendificator.nut script is a logic _script. It is located in the sendtor_manager template.)


So. I know that's a lot of info to take in, but I hope it helps. Definitely take your time while reading all that, and maybe you can have a look through some of Valve's scripts in the vscripts folder and try to understand them.

I was planning to add a more elaborate explanation about how the script itself works, but it's getting late right now so that will have to wait. I will come back on this later!

Oh wow, I came to this page to learn, Apparently, the minecraft mod, Computercraft, helps me understand! :thumbup: :lol: :kermit:

This... sentence... is... false.... dontthinkaboutdontthinkaboutdontthinkaboutdontthinkaboutdontthinkaboutit
MLIA
Thanks in advance :thumbup: