Have you wanted to get involved in trialmaking, but been too nervous because of the editor's expression engine? Have you wanted to do an investigation, but don't know how to get the variables to work? Do you have an idea for a new feature, but don't know how to implement it? Do you not know what I'm talking about? If the answer to any of those questions is yes, keep reading. I'm going to show the sort of things you'll be working with on a regular basis. Because of the nature of this tutorial, it's a long one, so please ask questions or read this in multiple sittings if you find it necessary. You may also find it helpful to create a sandbox trial and experiment with variables as I explain them.

What is a variable, and how do I make one?

First, we need to figure out what we're talking about. What are variables? A variable is simply something that has a name and a value. There are many possible names and many possible values. For instance, some variable has a name cat and a value dog. Weird, but valid. Some variable has a name s01 and a value 1. Valid. Some variable has a name variableAlpha. There's no value, so it isn't actually a variable. What about a variable with value variableAlpha? Without a name, it still isn't a variable.

How do these variables get their names and values, you ask? Just like in mathematics, we make them up. For instance, consider a simple word problem: Anna has 100 apples and gives Bailey some number. How many does she have left? We completely arbitrarily decide that some variable a now means the number of apples Anna gave Bailey. We could just as easily have named that variable y or made a mean the number of apples Anna has left. Some names and meanings are just more useful than others. (Note that where mathematics usually has its variables be pure letters, we can have variables be almost any combination of letters and numbers. Never start a variable with a number, though! That breaks the system!)

With this in mind, we're ready to make up our first variable. I'll give you a step-by-step procedure as well as pictures.

Spoiler : Images 1, 2, 3 :

* First, click on the "set action" button on the row where you want the variable to first show up. (Image 1)* In the action editor, select "Define a variable's value." It will be under the bold category "Manage variables." (Image 2)* Click "Add a variable" until you have as many sets of two boxes as there are variables you want to define.* Check "Advanced mode" and "Load from runtime expressions" boxes where appropriate. A discussion of when this should be checked will occur later. For now, don't click "Advanced mode," and don't look for "Load from runtime expressions."* Under "Variables to define" input the desire variable name and variable value. (Image 3)* Hit "confirm."

In this case, I set variable "contraB1" to mean "1". With this, you've set up a variable!

Can I change frame reading order based on a variable's value?

You may have noticed there are three other ways the editor allows us to manage variables. The first is through "Test an expression's value." For now, we'll consider it's specific application to variables. Used this way, it look at a variable's value. If it matches the value you want, it goes to a special frame. Otherwise, it goes to a generic frame. For an example of how to put this in and what it does, please look at the next image.

Spoiler : Image 4 :

* First, click on the "set action" button on the row where you want the variable to first show up.* In the action editor, select "Test an expression's value." It will be under the bold category "Manage variables."* Click "Add a variable" until you have as many sets of two boxes in "Accepted Values" as there are variables you want to define.* Check "Advanced mode" and "Load from runtime expressions" boxes where appropriate. A discussion of when this should be checked will occur later. For now, don't click "Advanced mode," and don't look for "Load from runtime expressions."* Under "Type of object to test," select "Variables."* Enter the name of the variable to test and the failure frame ID. This tells the player where to go if the variable doesn't match any of the values you set.* Enter the values and target frame IDs that you want. These tell the player which frame ID to go to if the variable is at that particular value. (Image 4)* Hit "confirm."

In this case, the variable contraB1 is checked to see if it has value 1. If so, it goes to frame 537, which then sends Edgeworth back into the cross-examination. If it doesn't, it goes to frame 538. On frame 538, contraB1 is set to 1. This structure is put in to ensure that the player gets the dialogue starting on frame 538 (Edgeworth explaining a contradiction) exactly once! Note that well-named variables can make it easy to understand what the variable is doing later. Without knowing anything else about the case, I can tell this variable refers to whether a contradiction 1 in testimony B has been found.

Also, where you put your actions can matter. Time for a thought experiment. What would happen if I defined contraB1 on 536 and moved the "test an expression's value" action to 538? (Do this once assuming frame 536 then goes directly to 538, skipping 537, and once without this assumption.)

Spoiler : Answer :

With the special assumption: The moment you got to frame 538, you would be directed to frame 537 and get sent back to the cross-examination without Egeworth getting to explain his contradiction!

Without the special assumption: Edgeworth never even gets to frame 538 before jumping back to the cross-examination start.

Now, you may have noticed the "Add" button.The add button allows us to create more special cases on the same frame, just like we could create more variables on the same frame earlier. For instance, instead of just having something fancy for contraB1 being 1, we could give a special redirect for 1 and another special one for 2. To see what it looks like, click the below spoiler.

Spoiler : Image 5 :

You can see here that if hoserName is edgeworth, it goes to 2310. If it's phoenix, it goes to frame 2319. There are more options, but there are so many that you need to scroll down to see them all.

The add button may be used to add special cases, and the delete button to delete them. Also, note that I made the "if none of these work" option be the start of the case. NEVER do this unless every option has a special case, and consider making the "generic redirect" leading to a frame telling the player to report a bug! Also, if a variable has not yet been set, it takes a value of 0 for purposes of actions that care about what the variable is set to.

And with that out of the way...

What is the expression engine, and how do I use it?

Here we get to the fun part, expressions. If you want to do something with variables that's more complicated than "if X has value Y, go to frame Z," you're going to be using it.

Spoiler : Henke's explanation of the expression engine :

The expression engine explainedAs a few posts have shown already, we got an expression engine. I felt that someone should give an userfriendly explanation on how it works.

Quick word list:Operator Thing that does stuff to one or more things. The plus sign is an operator.Operand A value that an operator operates on.Function A named chunk of code that does something. It may return a value.Argument or Parameter A value passed to a function for use by the function.Prefix Place some text before some other text or the text being placed before.Evaluate Fancy word for "Do" or "Check"Expression A string of values and operators that has a value.

As you might already know, it is a custom engine built by unas since he didn't feel like exposing the site to insecure usage of eval.The engine is fairly easy to use, it has a these operators:

+ Add. Add two numbers

- Subtract. Subtract one number from another number

- Sign flip. Flip the sign of a number

/ Division. Do an integer only division. Any reminders is thrown away.

% Modulus, aka reminder. Gives the reminder of a division.

^ Power. Gives the first number to the power of the second number.

& And. Returns a boolean that is true only if both operands is true.

| Or. Returns a boolean that is true if one or both of the operands is true.

! Not. Inverts a boolean. True is false and false is true.

= Equals. Returns true if both operands are equal.

< Less than. Returns true if the left operand is less than the right operand.

> Greater than. Returns true if the left operand is greater than the right operand.

. Concanation. Joins two strings together.

The operators have the following priority order:

!

^

*, / ,%

+, -

>, =, <, .

& and |

Lower value means that it is done earlier. Operators on the same row is evaluated in a left to right order.You can use parentheses to override the operator order by wrapping the sub expression with them.

If you want to do less than or equal, you got plenty of ways of doing it, you can use <, & and = to do the check, you can use < after adding 1 to the right side and you can use !(x>y). Do the same for greater than or equal.

There are several functions that you can use:

str_begins_with. Checks if one string begins with another.

str_contains. Checks if one string is in another.

str_ends_with. Checks if one string ends with another.

str_first_word. Returns the string up to the first space, the space not being included.

str_distance. Returns the percentage of difference between two strings. Returns 100 if they have nothing in common, 0 if they are identical.

evidence_is_revealed. Checks if a given evidence ID is revealed. You must pass the type (In French! 'preuve'='evidence' and 'profil'='profile') as the first argument.

random_int. Generates a random number. If you call f:random_int(), the number will be chosen between 0 and 100. You can also call f:random_int(5,10) to generate a number between 5 and 10, and so on.

get_date. f:get_date() returns the current date. The difference between two dates will be the number of milliseconds between them.

You can (and should) use variables in the expressions. Do this by just typing their names where you want their value.

That's certainly a lot of information, but when are we ever going to use it? Or can we even understand it? Well, this provides the first step in the incredibly powerful tool that is the "evaluate conditions to redirect the player" action. If you give it an expression, it will see if it's true or false and then take you to one of two frames, depending on what you get out of it. It's a lot easier to understand the expression engine if you see it in action, so I'll also take this opportunity to acquaint you with "evaluate conditions to redirect the player."

Spoiler : Image 6 :

* First, click on the "set action" button on the row where you want the variable to first show up.* In the action editor, select "Evaluate conditions to redirect the player." It will be under the bold category "Manage variables."* Click "Add conditions" until you have as many sets of two boxes in "Accepted Values" as there are variables you want to define.* Check "Advanced mode" and "Load from runtime expressions" boxes where appropriate. A discussion of when this should be checked will occur later. For now, don't click "Advanced mode," and don't look for "Load from runtime expressions."* Enter the failure frame ID. This tells the player where to go if the variable doesn't match any of the expressions you set.* Enter the values and target frame IDs that you want. These tell the player which frame ID to go to if the variable is at that particular value. The player will go to the first frame based on order of input that's true. Expressions here MUST compare two sides of an equation or inequality (Image 6)* Hit "confirm."

Let's look at this expression "pressA1=1 & pressA2=1 & pressA3=1 & pressA4=1 & pressA5=1 & pressA6=1" piece by piece. We start with "pressA1=1". We know that if pressA1 is 1, this will work out. (0 often means false, and 1 means true.) Now let's look at the & symbol. In order for this to give us the true case, which is equal to 1, we need all the other expressions to be true. Using this, we can pretty quickly say that all six statements in the cross-examination need to be pressed in order to go to frame 314. Otherwise, we're stuck at 207. If this seems familiar, there's a good reason. This is the procedure for a press-all-to-continue cross-examination. (Also, note that the best way to understand what henke wrote was practicing it. Don't hesitate to make a sandbox trial and experiment in the editor!)

There would be other ways to get the same result. For one, I could have instead typed "pressA1*pressA2*pressA3*pressA4*pressA5*pressA6=1" If I only have 0 and 1, that would also need all values to be 1 for the true case to occur. This sort of trick is going to be the primary way investigations work. Set a variable for each trigger, and write expressions for what happens when the write triggers are done. Note that when working with investigations in particular, keeping track of variables is extremely important. "step5=1 & step6=1 & step7=1 & step8=1 & step13=1 & step14=1 & step25=1 & step17=1 & step18=1 & step19=1" would mean nothing to me if I didn't have a written list of what each variable corresponded to.

There are some other interesting cases. For instance, sometimes you may need to use one of the functions. Let's say that after reading Ferdielance's fairness guide, you've found inspiration for your sadism and wanted to make a press conversation that relies on luck to decide if you get the new statement or not. You could define a variable as f:random_int(0,1) to make it a coin flip whether the poor player gets the hint or not. You would need a frame to read the variable's value and send the player either into the new statement or the trick end. Note that any time you use expressions outside of the Evaluate Condition feature, the advanced mode box for the action must be checked, and the "load from runtime expression" box must be checked for each part where you use expressions! This is usually only useful for blanks that allow you custom input of checking a box or selecting something from a dropdown. This is the general rule for checking the box. Also note that in functions, variable names must be in double quotes. Any time you use a string, put it in single quotes.

One last note here. Remember the "test an expression's" value function earlier? If you set type of object to test to "expressions," it can make a redirect based on the value of expressions instead of a variable. Expressions there cannot look like an equation. Input "pressA1* & pressA2" and set somewhere to go if it equals one instead of "pressA1*pressA2=1"

Are there any other special tricks I should know about?

There are three.

The first one is a bit more advanced. Credit to Ferdielance for publicizing this.

Spoiler : Using & and | to set variable values :

Suppose you want to do the following:

* If some condition is true, set variable Y to the value 'Well done!'* But if that condition is false, set variable Y to 'Oops.'

Normally, you might evaluate a condition, then jump to a frame that sets variable Y's value depending on the result, then proceed from the next frame out. But there's a faster way!

Consider how many frames that would have taken by any other means! Uses of this include, but are not limited to:

* Making more efficient examine-based engines.

* Giving the player a different message depending on how many penalties they've received (requires you to increment a penalty counter.)

* Making type-and-response engines that DON'T take up an insane number of frames.

Warnings:

* I don't know how much longer Unas will support this. It works in the v5 and v6 player, but the source of the v6 player says that Unas will probably rewrite the expression engine soon...

I don't think it's a security issue, seeing as the expression engine sanitizes everything quite carefully, but it's conceivable that Unas might decide that this behavior of the Javascript & and | is potentially risky in some way and switch to truly Boolean operators.

* Don't assume that evaluation always occurs in the obvious order. If, for example, you do:

returns 'Variable is undefined!' if the variable truly is undefined. However, the longer expression just doesn't return anything. This is probably because the engine processes the entire expression and evaluates functions even if they're on the wrong side of an OR. But I'm not really a programmer, so take my explanations with a grain of salt.

* Make sure you can read your code. Use parentheses and spacing consistently, and consider downloading Notepad++ or another text editor that automatically tells you whether your parentheses match. Don't make something that it will be a nightmare to follow later...

Whew!

The second one is the "store player's input into a variable" feature, often used as a password. (Feel free to play around with the expression options here, but there really isn't any reason why you would use these unless you want to make the variable name be dependent on other variables, in which case you would check the expression box for the name.) This is pretty straightforward and just requires you to input the variable name and the data type. "Strings" is any alphanumeric character with punctuation. "Word" is pure alphabet characters. "Float" is pure digits. To use this as a password, have another frame read the value of this password to see if it agrees with the values you want. Checking "password input" will make the player see dots instead of the individual characters when they type the password.

The third trick isn't a trick so much as the ability to think with variables. If you have a feature, how are you going to get it to work using variables? The rule of thumb is to think of what is important to what you want and creating variables to reflect this, making sure the variables reflect the conditions you want to control. Understanding this is critical for doing anything reasonably complex with variables.

To demonstrate, I'll give an example. Your mission is to figure out how you're going to implement a super-objection. The rules for a super-objection are as follows: a player is asked to present evidence. At any point, they may either choose to present more evidence, or they may object. When they object, if they have presented every "right" piece of evidence and have not ever presented a "wrong" piece of evidence, they move on to explaining the contradiction. If either condition fails, the player goes to the penalty conversation and may try again. Please think for a few minutes about how to do this before looking at the spoiler tag. Even play around with the editor to test it out! For simplicity, you may assume there are two pieces of right evidence, although the solution should generalize for any number of pieces of evidence.

Spoiler : Enthalpy's Solution :

We already know there's going to be an "ask for evidence" frame. We'll call this Frame 1. We also know that depending on the evidence we get, we can manipulate what frame the players goes to. So, what frame should the player go to?

Thinking in terms of variables, we need to make sure Good Evidence A presented, Good Evidence B presented, and nothing else has been presented. Those all sound like reasonable variables, so we now know that the evidence one presents should somehow go to a variable-setting frame.

Connecting our ideas, we have a general plan. Presenting Good Evidence A takes you to Frame 2, which sets some variable like superEv1 to be 1. Frame 3 will redirect to a later frame to avoid triggering the other variables. (We'll later find that 7 works out nicely.) Good Evidence B goes to Frame 4 and sets superEv2 to be 1. Five redirects to seven, and presenting anything else takes you to frame six, which sets superEv0 to be 1. Frame 7 will ask the player to either go back to Frame 1 and present more evidence, or object. If the player selects object, we take them to Frame 8. Frame 8 has a command to evaluate condition "superEv1=1 & superEv2=1 & superEv0=0" This accounts for all the variable behavior. (Remember, we want no bad evidence, and any variables that aren't messed with are automatically set to 0.) Success goes to the success conversation. Failure goes to the failure conversation, where the values of the three variables are set to 0.

Viola! That's the core of a super-objection!

And... That's it. Tutorial complete! It may feel a bit confusing, but it should start making more sense as you work with the editor. If you have any questions, please feel free to ask here!

As most readers have probably noticed, this is a modified version of my v5 tutorial. Let me know if you have any feedback, such as if something could be made clearer, or I don't explain a v6 feature well.

[D]isordered speech is not so much injury to the lips that give it forth, as to the disproportion and incoherence of things in themselves, so negligently expressed. ~ Ben Jonson

does, especially for one-off variables (variables you're only going to use once). However, I've seen people either do or advocate setting a variable to 0 before you need to use it. It's redundant coding and a waste of time. Technically, when we do those variable checks, the first thing the engine does (I hope I'm right when I say this) is that the variable exists. If the engine doesn't have the variable stored into memory, so to speak, the "value" of the variable is 0, as you have explained. You may want to add that, unless there is a reason to set your variable as 0, it is not necessary to set your variable to 0 before doing press-all variable checks. (something like that)

E.D.Revolution a écrit :Technically, when we do those variable checks, the first thing the engine does (I hope I'm right when I say this) is that the variable exists. If the engine doesn't have the variable stored into memory, so to speak, the "value" of the variable is 0, as you have explained.

That's true. The engine ensures that reading an unset variable will return 0.

However, I personally think it's good practice to initialise variables yourself at the beginning of the scene that uses them, for the sake of clarity.And it has some other practical aspects : notably, when set the variable will be visible in the debugger, allowing you to edit it easily without having to remember its full name.

I understand that. It's just that the engine does that by default, so why are you doing what the engine already did for you? It's just redundancy, a bad coding/scripting technique. But again, just a minor point.

Okay one more point. I have not seen a situation that would call for a concantation. I wonder if you could find a situation that would call for this. The last time I've seen a concantation was in Java programming.

Beautiful is better than ugly.Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!

@ ED: Suppose a character is entering a combination by typing one letter at a time. You want each number to remain its own variable, but you also want the combination the player enters as its own keycard. Concatenation can make a new variable value out of those old ones.

Regarding Variable Initialization: Most AAO coding is simple enough that I think you're fine with implicit definitions. I'll probably add a small paragraph on this issue tomorrow, after my thoughts have settled a bit more.

[D]isordered speech is not so much injury to the lips that give it forth, as to the disproportion and incoherence of things in themselves, so negligently expressed. ~ Ben Jonson

Okay one more point. I have not seen a situation that would call for a concantation. I wonder if you could find a situation that would call for this. The last time I've seen a concantation was in Java programming

All the Brooks and Soldiers uses concatenation in a few places! It's great for filling in certain blanks in pre-made strings, or doing "assemble the password" puzzles. My insane AAO programming tricks also use it, but that's not really a major use.

"A slow sort of country!" said the Queen. "Now, here, you see, it takes all the running you can do, to keep in the same place. If you want to get somewhere else, you must run at least twice as fast as that!"