I have a nice suggestion for the author also, since now we have the select unit function. Whenever the zergling die and create a new one, immediately select that one for me so I can start right away.

I helped Chrinux do a little testing and (he can correct me if I'm speaking out of turn here) that feature was originally in the map but was taken out partly at my suggestion. What can happen sometimes is that the prior level's commands can transfer over to the next level, causing you to run to your death inadvertently. Even removing all actions from the unit's queue can't properly prevent this as well as deselecting the unit can.

Same, but I would find it easier to adapt into this if you put the tutorial into a separate page with it's own menus, making it easier to backtrack for information.

Maybe include quick guides and tutorials for commonly scripted things people usually want (like ordering a unit to chase another unit or basic things like adding units into cargo). I could not spawn a medivac with 4 marines for example with the GUI seeing how it confuses the fk outta me. Each term listed in there might mean something different when programmer says them, and it never works out either because I used the wrong one, or didn't know how to set it up.

As for the quick guides, it should be more of a supply and demand, have a list with the most popular scripting need and create a guide for each one that covers it at the time.

For example:

Basic - How to create units in a location, over time, and with modified unit variables.

The reason why we need a huge variety of different guides, is because everyone seems to want to do something, but you never really can tell how they want to do it.

I'll probably post this in the wiki eventually to make it easier to navigate.

I will likely not include very specific examples unless they directly relate to a concept I am going over. This guide isn't about specific examples, it's about Galaxy. If you want to figure out how to do something, it works in exactly the same fashion it does in the GUI, minus the GUI. If you want fast examples, do something in the GUI and then look at the generated code.

Mapster also has a great API reference you can use to get information.

Part 2

This section of the guide will cover more advanced topics. Before continuing, make sure you are familiar with the basic concepts covered in Part 1. They will be used in more complicated scenarios and understanding their basics will be essential.

In unrelated news, the tutorial will likely take on a more personal style of writing since it's far easier for me to do.

This portion of the guide covers the following topics:

Using Galaxy in a Map - Various methods to use the knowledge you learn here.

Using Galaxy in a Map

There are a few ways to get Galaxy code into your map. One of the easiest ways is to create a "Custom Script" element in the trigger editor. This is done by going to Data->New->New Custom Script within the trigger editor.

Doing this allows you to type Galaxy script directly in the trigger editor. This is one of the best ways to get started with scripting and seeing how things work. These custom scripts will be inserted into your overall map script (as seen by Viewing Script - see Part 1) after any GUI global variables are declared and initialized. In addition, if you have multiple custom scripts, they will be inserted in the order they appear within the GUI. The editor will compile your script every time your map is saved. There is a slight delay between entering code and it being inserted into the map, so you may notice erroneous errors when saving quickly.

Galaxy code can also be inserted in the midst of GUI created triggers. There are many instances where custom script may be entered in place of values in the GUI. In addition, the trigger action "Custom Script" may be used to insert custom written code. There are a few caveats with this approach however: actions take place within triggers, which are functions. This means that code inserted in this fashion is placed in the middle of a function, limiting it somewhat (more details on this in the function section).

The Galaxy compiler is very cryptic and will give fairly terrible error messages when you make a mistake in your code, so compile often so that mistakes are easier to find.

The last way to introduce Galaxy code into your map is to import it. This is the best option for writing a lot of Galaxy code as you can use an outside editor with code completion and other features to make writing code less painful. Using this method, code must be imported using the Modules->Import dialog. Once a galaxy file or series of files is included in this fashion, it can be utilized by your map.

To use imported scripts, they need to be included using the include keyword. For example, if you have a galaxy file called "mycode.galaxy" and you import it into the root directory of your map, you would include it in the following way:

include"mycode"

Note that the .galaxy extension is excluded and that there is no semicolon at the end of this line. The code in your included galaxy file will be inserted where the include statement is located, so anything in your included file will be useable after the line at which it was included.

If you want to include a file immediately at the top of your script, you will be unable to do so using conventional methods (creating a Custom Script), because custom scripts are inserted after global variables and the like. The way around this is to follow the instructions listed in this tutorial. The basic premise is that you will edit your map's main MapScript.galaxy file to include the files you need and run any initialization routines necessary.

If you intend on using only custom scripting and no GUI functionality, you can easily get away with using Custom Scripts with include statements since there will be no risk of some GUI created global variable needing to reference them.

Accessing GUI created variables, functions, and triggers

If you've played around with custom scripts and attempted to use things you've created in the GUI, you may have run into syntax errors that at first glance look correct. The likely issue is how the GUI names its variables.

Global variables are prefixed by gv_.
Global triggers are prefixed by gt_.
Global functions are prefixed by gf_.

Local variables are prefixed by lv_.
Local parameters are prefixed by lp_.

In general, the naming scheme followed by the GUI tends to prefix variables based upon their scope (g for global, l for local) and the style of variable (v for variable, t for trigger, f for function, etc). When in doubt, take a look at the generated map script and look at the names.

Constant Variables

Before explaining constant variables, let's do a quick overview of variables. Variables can be either any of the built in data types (such as int, bool, unit, etc - see Part 1), structs we define, or aliases to these types (typedefs). We can also have arrays of any of these variable types. From this point on, the term variable will refer to any type of variable, whether it is built in, user created, or an array.

Constant variables differ from normal variables in that their value cannot be changed. If their value cannot be changed, you may be wondering how they can get a value at all! Constant variables can only be given a value if they are both declared and defined at the same time. Not only is this the only time they can be given a value, they must be given a value at this time. For example:

As can be seen in the prior example, the keyword for making a variable constant is const. Variables declared constant by use of this keyword cannot be used as member variables inside of a struct.

A Variable I Can't Change? What good is that?!

Constant variables are a great way to get rid of "magic values" in your code. Consider the case of creating a tower defense type map where the waves of enemies spawn on regular intervals. If the time of these intervals does not change, using a constant variable makes a lot of sense. If you change the variable, it will affect every portion of your code that uses it, making it easier to maintain and faster to update.

Structs and Arrays: Constants in Disguise

You may remember earlier mention that both arrays and structs could not be used in assignment. This is because both arrays and structs are non modifiable, meaning that their value cannot be changed. This is very similar to a constant variable!

The key difference here is that although the struct and array variables themselves cannot be modified, their contents can be changed, so long as the contents themselves are not considered constant/non modifiable in some regard.

int[5]someArray;// inside some function:someArray=3;// error, can't assign to an arraysomeArray[1]=3;// ok, changing the value inside of the array (int)

structmyStructType{intfoo;bool[2]boolArr;}// inside some function:myStructTypetest;test=test;// error, cannot use struct in assignmenttest.foo=3;// ok, foo is a modifiable (non constant) variabletest.boolArr=2;// error, cannot use array in assignmenttest.boolArr[0]=2;// ok, boolArr[0] has type int and is not constant

both result in errors, since it is redundant to have a constant array or struct.

Galaxy does not support arays of true constant variables (variables prefixed with the keyword const). In addition, structs cannot contain const member variables.

Multidimensional Arrays

Arrays can be of any type so long as it is not prefixed with const. Let's think about this for a second - this means we can have arrays of arrays!

Arrays of arrays are called multidimensional arrays, since they are easy to picture as various shapes increasing in dimensionality. Consider a simple, 1 dimensional array:

int[5]oneD;

We can picture the various indexes for this array in the following way, which resembles a line (which is one dimensional):

| 0 | 1 | 2 | 3 | 4 |

So what happens when we increase the number of dimensions? There are a few things to note before we move on to this. Arrays are just ways of storing a lot of the same type of variable - though we can have "arrays of arrays," it is not possible to mix types. So before we picture what a multidimensional array looks like, let's look at the syntax for it:

baseType[arrayDim1][arrayDim2][arrayDim3][...]myArray;// in generalbool[2][3]twoD;// a two dimensional bool array of size 2x3

As can be seen, increasing the dimensions of an array is simply a case of adding more brackets to its declaration. Each set of brackets increases the dimension by one. The total number of elements we can store in our array becomes the product of all of these values. For example

bool[2][3][4]threeD;// bool array of size 2x3x4, total elements = 24

We can picture a two dimensional array as a table of values (which looks like a rectangle - a 2D shape):

int[R][C]twoD;// an int array with R rows and C columns

| 00 | 01 | 02 |

| 10 | 11 | 12 |

Where the first number corresponds to the row index, the second number corresponds to the column index. We'd access an element from this array in the following manner:

twoD[row][col];

Increasing the dimensionality to 3, we can picture the array as a cube of values. Going beyond three dimensions requires some creative thinking if you wish to form a picture of the data, but this should not be necessary. The key way of thinking about array data is the following:

Each bracket of your array contains everything to the right of it. For example:

int[5][4][3]myArray;// where a, b, and c are arbitrary indexes:myArray[a]// This has five elements, each of which is an int[4][3] array.myArray[a][b]// This has four elements, each of which is an int[3] array.myArray[a][b][c]// This has three elements, each of which is an int

So whenever we want to use myArray in some statement, we need to actually access its base type by going through each dimension of the array until we reach it. If it is a 3 dimensional array, this will require three sets of brackets.

Multidimensional arrays follow all of the same rules as normal arrays. Remember that the elements inside of a multidimensional array may be arrays themselves (until you reach the base type), which follow the rules of arrays as well!

Functions

A function is a piece of code that performs a specific task. Consider a case of taking a case of setting a unit's health to some arbitrary percentage and displaying a UI message about this event. A function could be written to do this for any arbitrary unit. This is the example that will be used when going over functions.

Functions, like variables, have names. Function names can only be used to execute (call) a function. Unlike variables, function names cannot be assigned things or assigned

Here is the basic syntax for a function in Galaxy:

returnTypefunctionName(parameter_list){body_of_function}

Return Type

The first piece of information you must provide is the return type for a function. The return type can be any variable type that is allowable to return from a function (everything except for structs and arrays). Functions cannot return constant variables.

The return type of a function allows you to treat that function call as if it were a variable of that type. For example:

intsomeFunction(){return3;}// in some other function:intx=2+someFunction();// someFunction() has a return type of int and thus can be used as if it were an integer

In addition to normal types, functions can have a special type called void. Void means that the function does not return any information and cannot be used in expressions with other variable types.

Whether a function has a return (non void) type or not (void), it can be called on its own. The return value can be completely ignored. For example:

// using the someFunction defined abovevoidmain(){intx;someFunction();// valid, we ignore the return typex=someFunction();// valid, we use the return type}

One thing you may have noticed in the above examples was the return statement. The return statement signals that the function should exit immediately and return whatever value follows it. The expression returned does not need to be simple - it can be any arbitrary expression so long as it eventually evaluates to the return type of the function.

boolderp(){returnsomeFunction()>2;}

If a function has a return type, it must have a return statement that is reachable no matter what. What this means is that if your code has if statements (to be covered later) in it, the function either needs an independent return outside of this or needs one in an else statement:

boolherp(){if(true){returnfalse;// if this were the only return statement, this would be an error}else// the else must have no conditions{returntrue;// having this in an else case with no conditions satisfies the return requirement}returnfalse;// this return statement at the top level (outside of any if/while/else) also works}

What if I want to return more than one variable?

It will often be the case that you wish to have a function affect more than just one variable. While it is not possible for a function to return more than one variable at a time, nothing stops a function from altering a global variable.

So long as a global variable is defined before a function, that function may safely alter the contents of that global variable. In this way, it is possible to write functions that "return" many values at once, since they directly modify existing variables and do not need to return values to do so.

Parameter List

So now that we understand returning information from functions, let's discuss passing information to a function. In a function declaration, the parameter list allows us to do this. Parameters are optional and are not the only way to get information into a function. Global variables can also be used to get information into a function assuming they are declared before the function is.

Parameters look something like the following:

voidsomeFunc(type1name1,type2name2,...){}// in generalvoidsomeOtherFunc(intfoo,boolbar){}// a real example

The parameter list is a set of type name pairs that describe what kind of information the function takes in. These types can be any type that is allowed to be a parameter (all types except arrays, structs, and constants). Parameters have local scope to the function and cannot conflict with any local variables (more on this later) defined in the function.

When calling a function with parameters, you must pass values, or arguments for each of the parameters listed. For example:

voidaFunction(intfoo,boolbar){}// in some function:aFunction(3,true);// for each parameter in the function declaration, we pass an argument

The type of the argument must match the type of the parameter and they must be passed in the same order as the parameters are listed. The names do not need to match, but all parameters listed must be provided as arguments to the function call. These arguments can be literals (like the number 3, the value true, etc), variables, or any type of expression that evaluates to the proper type.

When a function executes, the parameters will take on the values of the arguments you pass in. Inside of a function, parameters are just like any other variable and can be used as such. One thing to note is that changing a parameter does not change the argument that was passed in. They are distinct variables that exist in different scopes.

Using Parameters

Let's consider the example of setting a unit's life to some arbitrary amount. Here's a function that does that:

If we pass some unit and some life amount to this function, it will change the life of that unit to half of the life we passed in. Note that though the parameter life changes in the function body, it would not affect whatever value we passed in as an argument.

A Note on Built in Types

Recall that I mentioned that changing a parameter does not affect an argument. While this is true in theory, many of the built in types do not work in exactly this fashion.

Consider the previous example of changing the unit's life to some value. The same unit that we pass in is the one that is modified. How can this be if it is a distinct variable? The reasoning for this is that many of the more complex built in types can be thought of as references to some object. When we pass in a unit, we copy that reference to the unit. If we changed theUnit to refer to some other unit, it would not affect the argument we provided. However, if we do not change its value, it still refers to the same unit that we passed in - thus we can affect it by calling functions such as UnitSetPropertyFixed().

Function Body

The body of a function is the place where you write all of the code for the function - the stuff that actually makes it do something!

If you declare any variables inside of a function, they have local scope to the function and cannot be seen outside of it. All variable declarations in a function must be the first thing that happens in a function. In addition, these variables must all be declared before a single one of them is defined.

Local variables declared and defined properly:

voidmyFunc(){intherp;boolderp;// one declaration per line at the beginning of the functionherp=3;// all definition done after all variables to be used are declaredderp=true;}

A function with mistakes:

voidmyError(){intherp=3;boolderp;// this will now cause an error since we've defined a variable// some code hereboolfoo;// this will cause an error as it is not at the top of the function}

The variables declared inside of a function share the same scope as any parameters. This means that both local variables and parameters can only be read from inside the function and must not share any names.

Local variables and parameters with the same name as a global variable take precedence.

inthello=2;voidmyFunc(){inthello;hello=3;// the local hello takes precedence over the global one,}// the value of the global hello is still 2

As for the rest of the code inside of a function, what goes in there is entirely dependent on the purpose of the function. Function bodies consist of as many statements as you would like doing whatever they need to. Examples will be provided later in the tutorial when we introduce more complex concepts.

Programming Style

As you write code in Galaxy, it is useful to keep a consistent style and format your code well. This will make it easier for you (and others) to read and maintain.

There are many different ways of indenting your code. Take a look at this article. My personal preference is the Allman style.

A great article on coding style is located here. It covers many facets of well written code.

The second part, which I'm writing right now, will cover getting galaxy into a map. As far as an in depth tutorial covering writing something very specific in Galaxy, this guide will likely not cover that - it is more intended as an overview of the language with small examples for each new feature introduced.

I'm not sure who you want to address with this guide - is it beginners who don't know anything about scripting or is it intermediates who just don't know their way into Galaxy yet?

If it is for intermediates then I don't really understand why you put things in like how to make variables or how to do simple arithmic with them. That's one of the very first things you learn when you start with any programming language.

The beginning parts are addressed at beginners but it will ramp up and assume you've understood the basics as I progress on. It's not a reference but rather an increasingly complicated guide to doing things in Galaxy.

As for the other things you bring up that it is missing, this is only part one. I don't have the patience to write all of that for one big guide so it'll go up in parts. In terms of things being a little confusing, in general they will be explained further along in the guide. I'll talk about parameters and return values when I talk about functions - I just namedrop to begin with. I have to do that somewhat or I'd be stuck constantly explaining concepts.

This is a great starter to scripting. I'm so over the click click type of finding functions. Just a quick Q though, if you said the view script window is deleted and refresh on gameInit with a fresh copy from the visual trigger editor, how do we get our typed scripts into it then?

A Guide to Galaxy

The purpose of this guide is to acclimate people with the Galaxy language and custom scripting in Starcraft II. If you notice anything amiss or have suggestions, don't hesitate to let me know. This guide is not intended as reference and as such will build up from simple to more complicated concepts.

The beginning parts of this guide are aimed at beginners to programming. Later portions will assume that you understand the earlier material and do less hand holding.

Arrays - What they are and how they are different from normal variables.

Structs - What they are and how to use them.

Typedefs - What they are and how to use them.

Scope - Where variables and functions can be accessed in code.

Galaxy vs. GUI

Once familiar with Galaxy it can be much faster to develop in. The GUI editor provided is very powerful but incredibly cumbersome to use, something that can be avoided by developing in Galaxy. It is much easier to re-use code when developing in Galaxy compared to using the GUI.

The GUI provides certain advantages over scripting. It creates syntactically correct code and makes referencing pre-placed objects in your map much easier than in scripting (more on this later).

A Quick Note on the GUI

You can view the code generated by the GUI by opening the trigger editor and going to Data->View Script (CTRL + F11). You can change the display of the triggers to show you the actual Galaxy API calls by going to View->View Raw Data (CTRL + D) in the trigger editor.

You cannot edit the code you see in View Script directly as it will be erased anytime the map is saved and replaced with a generated version of what is in your trigger editor.

Details on how to incorporate custom scripts into your map will be provided in a later section.

General Galaxy Semantics and Concepts

Galaxy is semantically very similar to many other programming languages, most noticeably C. The actual semantics are something that won't be dealt with in tremendous detail in this guide as it can be learned indirectly from looking at code snippets and experimentation. However, there are a few important things to know:

Variables and functions must be declared before they can be used. This means that in order to use some variable foo, Galaxy needs to know what type of variable foo is before it is used. If there is some function bar(), it must either be declared or defined before its use as well. This will be explained in greater detail throughout the guide.

Commented code (prefixed with //) do not have any effect on the output of code - they are visible to the programmer but will not be looked at by the compiler.

A Quick Note on Terminology: Definition vs Declaration

Declaring means saying what something is, while defining is giving meaning to what that something does. Things can be declared and defined at the same time. What is important to the compiler is that a declaration exists before a variable or function is used.

Variables

Variables allow you to store values for later use in your script. The following is an example of a variable declaration:

intfoo;

This line creates a variable of type int (integer) named foo. This variable name can then be used in place of an integer to the same effect. For example, consider the following:

foo=2;// stores the value 2 inside of the variable foofoo=foo+3;// adds the variable foo and the number 3, returning 5 and storing the value in foo

Variables are assigned default values if you do not give them a value. In general it is good practice to initialize any variables you plan on using.

Arrays

Arrays allow you to group together a group of the same type of variable under one name. For example, consider the case of creating an RPG with a character selection and wanting to know whether players had chosen their hero. A naive approach would be to create a boolean variable for each player and write the code to change each one individually. A much better approach would be to create an array of booleans which could be indexed by the player number and changed accordingly.

The following is an example of creating an array:

bool[8]selectedHero;

bool is the type of the array, 8 is its size, and selectedHero is its name. This is the same syntax for creating any type of array in Galaxy: type[size] name. What this does is to create a single variable, named selectedHero, that can store up to eight different boolean values.

Once we have declared an array, we can access its members so long as we do so within a local scope in the following manner:

arrayName[index];// in generalselectedHero[1]=true;// for the example

One important thing to note is that Galaxy is a zero index based language. This means that the start of any array is 0, not 1! selectedHero has eight elements indexed by 0, 1, 2, 3, 4, 5, 6, and 7 - not 8.

Other than their syntax, arrays can be treated in almost in the same fashion as normal variables. However, they cannot be passed or returned from functions (more on this when functions are introduced). In addition, arrays cannot be initialized at the global scope and cannot be assigned to other arrays. What this means is that:

int[5]a;int[5]b;a=b;// error, arrays cannot be used in assignment

Structs

Structs, or records as they are known in the GUI, allow you to create complex variable types that can contain other variables. Structs are similar to arrays in that they cannot be passed to functions or returned from them. Also, like arrays, structs cannot be used in assignment and cannot be accessed in the global scope. Unlike variables, an initial definition of a struct must be provided before it can be used as a variable type.

The following defines a struct:

structmyStructType{intfoo;boolbar;};

// In general, this is how a struct is defined:structstructTypeName{// fields go here};

In the above example, myStructType is the struct name and foo and bar are struct member variables. With the above lines of code, a new variable of type myStructType has been defined and can now be used to declare variables.

To use this struct definition, follow the rules for creating a variable of any type:

myStructTypesomeStruct;

This previous line creates a variable of type myStructType named someStruct. Remember that since this is a struct, it cannot be passed or returned from a function. However, the struct member variables can be passed to functions so long as they themselves are not structs (yes, a struct can have a struct as a member variable) and assigned to normally.

To access the fields inside of a struct, use the following syntax:

someStruct.foo=3;someBoolVariable=someStruct.bar;// where it can be assumed someBoolVariable was a previously declared bool

Accessing struct member variables is only possible inside of a local scope, though the struct variable itself can be global.

A Quick Note on Structs: What are they good for?

Structs are excellent ways to organize variables that have a strong relation to each other. They are especially useful ways of organizing large amounts of data into an array. An array of structs can create a data type that is easier to use than several normal arrays. Consider an RPG where the programmer wants to keep track of whether each player has picked a unit, which unit they chose, and how many times they have died. One way to do that with a struct would be the following:

structplayerInfo{boolselectedHero;unithero;intnumberOfDeaths;};

playerInfo[6]playerStructs;// in some function:playerStructs[1].selectedHero=true;playerStructs[1].numberOfDeaths=0;

Typedefs

Typedefs allow the programmer to come up with aliases (different names) for variable types. They use the following syntax:

typedeftypename;

Where type is some variable type, either native or user created (a struct or some typedef), and name is the new alias for this type. The type used in this statement can now be referred to by either its original type name, or the newly assigned alias.

typedefintplayer;playerfoo=1;

Typedefs are useful for making code easier to understand. A script that relies heavily on integers to represent something very specific may be easier to develop and certainly to understand if a relevant typedef is used.

Scope

Scope refers to where in the code a variable or function can be used. There are three types of scope in Galaxy: local, global, and file.

A variable with global scope can be accessed anywhere assuming that the variable has been declared on a line prior to where it is used. To declare something in the global scope, simply write it outside of a function (at the so called "top-level") of your script.

intfoo;// not in a function thus has global scope

A variable with file scope can be accessed anywhere in a specific galaxy code file assuming that the variable has been declared on a line prior to where it will be used in the same file. To declare something with file scope, write it like a global variable and prefix it with the keyword static

staticintfoo;// not in a function thus has global scope// however, static keyword means this variable can only be accessed in the current galaxy file

A variable with local scope cannot be accessed outside of that local scope, usually meaning the function it is declared in. For example:

voidfoo(){intbar;// has local scope, declared inside of a functionbar=3;// ok, bar exists in the local scope of the function foo}bar=3;// error, bar does not exist in the global scope

You can't use non constant variables in events when using the GUI. Events are registered as soon as your map starts up, meaning they can't change throughout the course of gameplay. If you make your variable a constant, it will show up in the event dialog. However, this does little more than manually selecting the unit in the event dialog itself.

If you want your event to respond to different units throughout the progression of the game, you need to take a different approach. One method to consider is attaching a point and using the Enters distance from Point event. Other ways involve custom scripting.

All placed objects in the editor are stored in XML files (Objects and Regions) and are only accessible through Galaxy by a numerical ID. When you are using the GUI, it does the mapping from the name associated with that ID in the XML and pulls up the number.

So to answer your question, it is not possible to do this without either having someone use the editor to select the point, or manually translating from the named point to its numerical index for every map you want to do this in.