[E2] Beginner E2 Guide

Before I start, I want to credit “GinnyWeasley” who posted this on the old forums, to which I accessed via archive.org/web


The basics - The first six lines

When creating a new E2, you’ll be met with 5 lines of code, that you typically see on all E2s you access, some of them are optional, however, it’s common most of these will be used.

@name My first e2

This one is fairly straight-forward! @name sets the name of the e2, which you see when you look at it and is also the default save name.

@inputs

When you link up your e2 with other wiremod items and entities, inputs are the things which you see when you look at the chip. As the name suggests, they allow other items to input data (we’ll look at the different data types later on) into the e2 code, to be used for whatever purpose you need. @inputs only deals with wired inputs - there’s other ways of adding information that do not need to be declared, such as find functions.

@outputs

Similar to @inputs, outputs are the things you see after you’ve selected something else. These allow the chip to directly influence other pieces of wire. Note that not only wiremod entities have wire properties - many others, including keypads and fading doors, also have wire connections that can be made. For example, I can wire ‘Fade’ on a fading door to an E2 output - that’s the basis of a smart-opener code. As with @inputs, this is only for wired outputs.

@persist Test:string

If you already know about coding, @persist is where global variables are stored, as well as variables that are kept between each code execution.
If you don’t, then @persist stores variables (again, read on to find out more) which can be used many different times, in different blocks of code, and which will keep their value between each time you run the code.

@trigger

@trigger is one of the more-complicated and less-used @lines. It basically influences which inputs can cause the code to run. For example, @trigger all will mean a change in any input will run the code. Generally however, it’s not used very much.

@model

Pretty self-explanatory. @model sets the model that the chip takes. For example, you might create a radio, with 'models/props_lab/reciever01b.mdl' as the model just to make it look a tad better. @model isn’t included in the first 5 lines by default - you’ll have to add it in if you want to use it.

NB: When using @inputs, @outputs, @persist they will need to be typed. What this means is, the variable that you are wanting to place in the @line will have to have its type linked to it, for example:

@persist Owner:entity

The global variable Owner is bound to type entity


Data Types

MyName = "Chik"

In this line, MyName is a variable. For those who don’t know, a variable is a piece of memory that can be assigned a value, which can the be used, re-used and adapted as many times as you want. The value here is "Chik". This is a data type known as a string, because it’s a string of characters. Easy, right? In E2, as with many other coding languages, strings are encased with double quotation marks - "<string>". Different to other languages, however, single speech marks will NOT work.

MyAge = 87

MyAge has ben assigned the value 87. Which is, you guessed it, a number! That’s what it’s called. In E2, numbers may alsobe referred to as normals, which will hopefully be explained later.

MySelf = owner()

This is an entity. There are many different types of entity in GMod. Almost anything can be an entity, provided it is linked up correctly. Players are also included in the entity data type. This means they can have certain things read about them, for example their name. Entities are pretty hard to use unless assigned to a variable - they can’t be written normally. The owner() shown here is just a function telling the variable who spawned the e2.

MyVec = vec(0,0,0)

This is a data type known as a vector. In E2, vectors have a number of uses - they can represent positions, angles, colours, and much more besides. This is useful for creating programs based on WHERE something is in relation to something else. Vectors can have between 2 and 4 arguments, dictated by saying vec2() or vec4() when creating them. vec2 generally determines X-and-Y co-ordinates, and vec4 is generally for colours with a transparency needed.

MyRanger = rangerOffset(1, entity():pos(), entity():forward())

Damn, these things have obvious names, don’t they? A ranger, just like a standard wire ranger, is a (invisible) beam which can detect an entity, and return anything about it, depending on what you ask it for.

#MyComment
#[
    This is a
    multi line comment
]#

A comment isn’t a data type, but I’ll need to explain it here. Comments are lines of text that are ignored by the chip, allowing you to write things that aren’t actual programs. Every single bit of tutorial in here is a comment. In E2, there’s two ways of writing comments. Comments are also useful for cutting out specific pieces of code, for example when trying to fix errors.

@inputs EGP:wirelink

Wirelink is special. You’ll notice that it’s declared in @inputs. Wirelinks are a way of connecting an entity to the E2, giving a two-way relationship. Using a ranger, we could find out what an entity’s class was. Using a wirelink, we can find out everything about it, relay inputs back to it and control it on a far greater scale. One way of creating an E2 fading door is by connecting the fading door entity to a wirelink.

MyNorm = 1

In many coding languages, there’s a data type known as a Boolean. Quite simply, these are true-or-false statements. In E2 however, booleans are represented by a normal. Basically, if a number is 0, this equates to false. Any other number (typically 1) is true.

MyArray = array("dog", "cat", "panda")

An array is a form of data type known collectively as an aggregate. This basically means it can store more than one thing at once. This array, for example, has three different strings within it. To access an element in array, you use the syntax:

Arrayname[index, data type]

The index of an element is its position in the array. For our array above, index 1 is "dog", 2 is "cat" and 3 is "panda". In e2, the data type you’re getting from the array must be included. For example:

MyAccessFail = MyArray[1,entity]

Would not work, because the first element in the MyArray that we created is a string, not an entity.
This would create an error that prevents your e2 from running (hence why I had to comment it out. Where as:

MyAccessWorks = MyArray[1,string]

Would store the value "dog" to the MyAccessWorks variable, because "dog" has both the index 1 and the type string. There are a few limits on what data types can be stored in arrays. For example, tables (another aggregate data type) and arrays cannot be stored in an array.

Tables : TODO


Control flow structures

If / Elseif / Else statements
if statements are blocks of code that are only executed if a certain condition is met. For example, you might have a smart opener, which only opens IF a certain player is nearby. The syntax for an if statement is:

if(<condition>){
    <code to be executed>
}

The condition in an if statement can be almost anything. It could be a numerical comparison, seeing if a player is allowed to do something, almost anything.
The outcome on a comparison is a normal data type. This means putting:

if(1){
    print(MyAccessWorks)   
}

Would immediately work, because a normal 1 is equivalent to ‘true’ Similarly, if(0){} wouldn’t do anything, since 0 equates to false.

Next up, adding an ‘elseif’ section.
elseif is a second comparison to be made, that is only checked if the first one evaulates to false. If the first condition is true, all elseif/else will be completely ignored. Other than that, elseif is completely identical to if. It takes a condition, evaulates it, if true then it runs a block of code. Also, if an elseif is true, all other else/elseifs will be skipped. ‘else’ only runs if no other statements evaluate true. A fully typed-out if/elseif/else statement might look something like this:

X = 3
if(X==1){
    print("Not executed - X isn't 1")   
}
elseif(X>5){
    print("Not executed - X isn't greater than 5")   
}
else{
    print("Doesn't really matter what X is - else will be run without condition if everything else is false!")   
}

Comparators

Comparators are used to compare two things between each other, below is a simple list of the comparators you can use in an E2:

<  Less than
>  Greater than
<= Less than or equal to
>= Greater than or equal to
== Equal to (Using a single equals is for assignment to a variable, not a comparison.)
!= Not equal to

Logical operators

Logical operators allow you to have multiple conditions for an if statement, and to affect how these conditions are evaluated.

& - AND: returns 1 if BOTH conditions are true, 0 otherwise.
| - OR:  returns 1 if EITHER condition is true, 0 if both are false.
! - NOT: returns 1 if condition is false, 0 if it's true.

Putting these into an example:

X=1
Y=0
if(X & !Y){
    print("LOGIC!")   
}

This if statement would run. First, it finds the value of Y (0 for false) and inverts it. This gives us both X and Y as 1. Next, the & operator looks at both statements. Since they are BOTH true, the code will execute. If X was 0 or Y was 1 then we would need to use an OR gate to get this to run, as only one of the statements would be true.

Switch statements

Switch statements are a little known replacement for the repetitive if/elseif/else structures. Switch statements use a ‘case’ system to test conditions.
It’s worth noting that they aren’t QUITE as diverse - where two potential conditions are completely unrelated, for example, they can’t be used very easily.
Using X from before, a switch statement might look like this:

switch(X){
    case 1,
        print("Not executed - X isn't 1")
    break
    case X>5,
        print("Not executed - X isn't greater than 5")
    break   
    default,
        print("Doesn't really matter what X is - default will be run without condition if everything else is false!")
    break
}

The X>5 case demonstrates one way in which switches can become more complicated. Because the comparison operator needs a statement either side of it, it was necessary to put in X there, even though it was in the switch statement’s argument. In this way, switch CAN be used for unrelated conditions. It’s just a lot more confusing. It is required to have at least one argument for a switch statement, whether it is used or not. The ‘default’ case is equivalent to else - it is run when none of the other cases are fulfilled.
Put generally, switch statement syntax is:

switch(<variable>){
    case <value>,
        <code for this condition>
    break
    case <value>,
        <code for this condition>
    break
    default,
        <code to be executed in the absence of another true case>
    break   
}

Switch statements also have the ability to fulfill more than one case if no breaks are included between them, in a cascading structure:

X = 6
print("Greater than...")
switch(X){
    case X>5,
        print("5")   
    case X>3,
        print("3")
    case X>1,
        print("1")
    case X>0,
        print("0")
}

Here, X fulfills every comparison made. Since no breaks are included, the code for every case is run. If we had included breaks, only the first condition to be fulfilled would be run.


Control flow - part 2 Loops

While loops
As the name suggests, while loops keep repeating a section of code while a certain condition is true. This is useful for when you don’t have a certain number of repeats to do. While loop syntax is:

while(condition){
    <lovely lovely code to repeat>   
}

NB: MAKE SURE SOMETHING WILL EVENTUALLY CHANGE YOUR LOOP’S CONDITION TO FALSE. IF IT DOESN’T, THE CODE WILL REPEAT FOREVER.

Example while loop:

X=1
while(X<10){
    print(X:toString())
    X++   
}

For reference, the toString() just takes a number and makes it a string, and X++ makes X increase by 1 - so that eventually, X will be larger than 10 and the loop will end.
So, the second that X>=10, the loop is broken and the code will move on to the next part. Until then, it keeps going round and round.

For loops
for loops are the other type of loop in E2. They execute a code block repeatedly, but they do it a fixed number of times. For loops look like this:

for(I=0,10,2){
    print(I:toString())   
}

This code will take a local variable (one which is only used in this loop and loses is forgotten afterwards) and assign it a value of 10. From there until it reaches 10, it will print out its

value, then increase by a step of two.The ‘I’ in this loop represents the variable known as the ‘iterator’. The iterator in a loop is the number that will change and determine how many times the loop runs.
Generic for loop syntax is:

for(<iterator>=start,finish,step){
    <code to be looped>
}

If no step is specified, it will default to 1, meaning that the following code won’t work:

for(I = 10,1){
    print(I)   
}

The code is cancelled after a single iteration - the only output to the chatbox is 10. To fix it, I would simply have to put ``-1` at the end of the brackets. As with while loops, it’s important to have a condition that won’t keep going forever. While a for loop will always break eventually, having too many
iterations will make your code really inefficient.

All control flow structures in E2 can be embedded. This means you can place one inside another to accumulate the effects. Examples:

for(I = 1, 10){
    for(Q = 1, 10){
        print(I, Q)
    }
}

if(X<0.5){
    H1 = 999
    if(Use){
        H1 = 900   
    } 
}
1 Like