Article: Basic Scripting For Complete Beginners

Error message

Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /var/www/pied-piper.ermarian.net/includes/common.inc).
AuthorTopic: Article: Basic Scripting For Complete Beginners
Off With Their Heads
Member # 4045
Profile Homepage #0
Article: Basic Scripting For Complete Beginners
by Kelandon (tomwatts@berkeley.edu)

Did you read the BoA docs and not understand a word? Are you new to programming but have a great idea for a BoA scenario, if only you could learn to script? Have you never made a scenario before and don't quite know where to start amidst those hundreds upon hundreds of calls in the Appendices? If so, this article is for you. It has some of the basic scripting techniques to get you going.

Many have lamented how hard BoA is to learn compared to Blades of Exile, the older Spiderweb scenario-design engine. I will make frequent reference to tasks in BoE and how they are now accomplished in BoA.

MESSAGE_DIALOG
The first (and simplest) thing you should learn is how to use the call message_dialog. It pops up text on the screen. A simple message_dialog looks like this:

beginstate 10;
message_dialog("This text will pop up in a dialog box.","");
break;
When state 10 is called, a dialog box will pop up showing the text in that string. Since the second string is blank (it consists of ""), it will not be displayed, but if you put something in there, it will show up, too.

beginstate 10;
message_dialog("This text will pop up in a dialog box.",
"This text will show up, too!");
break;
The second string will be separated from the first string by a double-spaced break. Try it if you want to see what it looks like!

This is how you can communicate with the player, to explain the sights, sounds, smells, and tastes that the characters are experiencing in the game. Every time state 10 is called (which usually will happen when the player steps in one of those blue rectangles in the editor, an event rectangle), this message will pop up. But what if you don't want it to show up over and over again? That's what One-Shot specials are for, covered later in this article.

SDFS
Stuff-Done Flags (SDFs) are the basic way of keeping track of whether something has been done or not. They are, as the docs describe, essentially a grid of numbers that is sized 30 by 300, with each number able to range between 0 and 255. The docs have a fairly good description of them, so I won't say more than that you should have some handle on what they are before continuing with this article.

A QUICK NOTE ON CALLS
The docs make a lot more sense if you can organize the categories of calls. There are basically four kinds of calls in BoA: voids, shorts, flow controllers, and object characteristics, as well as a few formatting calls like begindefine[x] that aren't used very often. The calls in the appendices are either preceded by the word "void" or "short," depending on which they are, and these are the main calls that BoA scenarios use. A void does something. A short returns a number and also may do something. (We'll look at exactly what it means to "return a number" in a moment.) A flow controller tells BoA which calls to run. Object characteristics are for custom object scripts, and they aren't a big concern at first.

All you need to know for the purposes of starting out, though, are the above definitions of voids and shorts: a void is a call that does something and a short is a call that returns a number and also may do something.

IF-THENS
If-Then nodes were the way that BoE checked whether something was true or not. BoE only had a few (twenty-six, to be exact, but some of them didn't work). BoA has a slightly more complex format. BoA can check whether anything that can be represented as a number is true or not. And anything that is preceded in the Appendices by "short" is a number. So how do these work? Let's take a look at a basic example.

// If-Then Stuff Done Flag?
if (get_flag(0,0) == 1)
This checks if flag (0,0) is set to 1. If it is, then whatever comes after the "if" call happens. That's the "then" half of this.

// If flag (0,0) is equal to 1, pop up a message_dialog saying so.
if (get_flag(0,0) == 1)
message_dialog("Flag (0,0) is equal to 1.","");
Now if flag (0,0) is set to 1, a dialog box will show up. Let's look at the nuts and bolts of exactly how this happens. Most of the time you won't have to think about this, but it's good to know the specifics of what's going on.

Note, first of all, that TRUE is the same thing as 1 and FALSE is the same thing as 0. Basically, an "if" call checks if the next thing after it is TRUE. So, for instance,

if (TRUE)
message_dialog("True.","");
will ALWAYS pop up the dialog box saying "True." Note that the parentheses are necessary, because BoA will give you an error message without them.

So how do we get from (get_flag(0,0) == 1) to just TRUE, then? Well, (get_flag(0,0) == 1) is a true statement if flag (0,0) does in fact equal 1. You can think of it this way: BoA will simplifies expressions. It will replace get_flag(0,0) with flag (0,0)'s value, and then it will replace the resulting expression with FALSE if it is false or TRUE if it is true. The thought process looks like this:

// This is NOT REAL CODE. This is to demonstrate what simplification looks
// like.
if (get_flag(0,0) == 1)
// Now let's assume that flag (0,0) is 1. Replace get_flag(0,0) with the
// value of the flag.
if (1 == 1)
// Now, since the expression is true, replace it with TRUE.
if (TRUE)
// This will do the next call or, if you use brackets, the next set of
// calls.
Let's try this with another example, because this can be a bit confusing at first.

// This is, again, NOT REAL CODE. This is another example of simplification.
if (get_terrain(45,50) == 128)
// Now let's assume that the terrain in space (45,50) is terrain number 128.
// Replace get_terrain(45,50) with that value.
if (128 == 128)
// Now replace the true expression with TRUE.
if (TRUE)
And this will do whatever comes after it. Let's look at a different kind of example, one in which the expression is false.

// This remains NOT REAL CODE. This is more simplification.
if (get_level(0) > 50)
// Let's assume that character 0's level is 32, which is what get_level is
// checking. Replace get_level with its equivalent value.
if (32 > 50)
// Replace the false equation with FALSE.
if (FALSE)
// Thus the "if" will NOT do the next thing after it.
This suggests a small shortcut to this process. If you have a short and only care if it's zero or non-zero, like is_combat, then you can do this.

if (is_combat())
// whatever your special is
Let's do the same sort of simplification that we did before and see what happens.

// This is STILL NOT REAL CODE.
if (is_combat())
// Now let's say the party is in combat mode. Replace is_combat() with 1.
if (1)
// Now replace 1 with its equivalent, TRUE.
if (TRUE)
// which is exactly the same as before. It will do the next call.
That means that the following two statements are exactly the same.

if (is_combat() > 0)
and
if (is_combat())
BoA does this same process with any and every call that is a short, including some of the ones that don't seem like numbers (for instance, approach_waypoint -- this is one of those calls that returns a number and also does something).

As a sidenote: strictly speaking, "if" checks if the following argument (the thing in parentheses) is FALSE, and doesn't do the next code if it is. Thus, that if (2) is just as true as if (1). That means that if all you want to know is whether a flag has been set to a non-zero value but don't care what that value is, you can do this:

if (get_flag(0,0))
// your special here
That is the same as:

if (get_flag(0,0) > 0)
// your special here
Again, most of the time, you won't need to know any of these more complicated details. Most of the time a simple "if (get_flag(0,0) == 1)" will suffice. It's just handy to have a description of what's going on so that you can refer to it, and the docs have nothing (NOTHING!) on this.

ONE-SHOT SPECIALS
One-Shot specials are events that occur once and then don't happen ever again. This was an entire category of nodes in BoE, so it was easy to do. It takes a bit more thought in BoA. One basic format is this:

// One-Shot special
if (get_flag(0,0) == 0) {
set_flag(0,0,1);
// whatever your special is after this
}
This would, of course, be preceded by a beginstate and followed by a break in a real script, but as we are now talking about parts of a state rather than full states, I will omit those. Let's examine the logic of this One-Shot.

The first time this state is called, the flag won't have been set, so the "if" call will be true, and the following code in brackets will be run. The state will proceed to set the flag (0,0) to 1. Then it will do whatever follows that. So far so good.

The second time the state is called, the flag will have been set, because it was set the first time. Thus, the "if" will not be true and whatever is in brackets won't be done.

(Obviously, for each One-Shot you should use a different flag, or else once one has been called, every One-Shot that uses that flag will stop being called.)

There is an alternate way of doing One-Shots, namely:

if (get_flag(0,0) != 0)
end();
set_flag(0,0,1);
// whatever your special is after this
I do this most of the time because the other way can look ugly you want to check several things at the same time. If you only want the cut scene to run if the party has special item 4 and flag (0,4) is set to 2 and the terrain on space (10,14) is number 121, I think it's a lot easier to read

if (has_spec_item(4) < 1)
end();
if (get_flag(0,4) != 2)
end();
if (get_terrain(10,14) != 121)
end();
than it is to read

if ((has_spec_item(4) == 1) && (get_flag(0,4) == 12) && get_terrain(10,14 == 121)) Tests show that they perform equivalently even in the most extreme of circumstances, however, so you can use whichever one makes most sense to you.

DIALOGUE
Many new designers struggle with dialogue because they don't expect to have to handle much code in order to write it. While it is not as code-heavy as states in a town script, dialogue still contains code, and you have to understand a few basic commands in order to write it.

The unit of dialogue scripts is the "node," rather than the "state" that you'll find in almost every other script (except custom objects scripts, which contain "definitions"). Nodes consist of at least six things: a begin, a state, a personality, a nextstate, a question, and texts. I'll discuss each in turn, albeit somewhat out of order.

Begins are easy. Each node in each town is numbered from 1 to 99, and the first part of every node is a begintalknode call followed by the number. It looks like this:

begintalknode 1; Next come the definitions. Each node contains a series of definitions that tell what the node is. They look like this:

state = 1;
personality = 11;
nextstate = -1;
question = "Who are you?";
text1 = "_My name is Skassa._";
Personality is simple. It does very little. Basically, you give each character in your scenario a unique personality number. This is relatively easy to keep track of if you use ten personalities per town, so that town 0 contains personalities 0-9, town 1 contains 10-19, and so on. Of course, the numbers are completely up to you. Every dialogue node with that character should use the same personality number.

For the rest of this, it can be a bit confusing, so let's work through an example. Here is some sample dialogue, from Valley of the Dying Things. (It has been simplified slightly for the purposes of this example.)

begintalknode 38;
state = -1;
personality = 3;
nextstate = 31;
question = "Avizo";
text1 = "You meet a young magician, dressed in worn robes. He is clearly quite ill.";
text2 = "He doesn't look like he wants to talk with you. He coughs, and gasps slightly. _I'm Avizo. What do you want?_";

begintalknode 39;
state = 31;
personality = 3;
nextstate = 32;
question = "Are you on a mission here?";
text1 = "_No. Not anymore. I am trying to flee the Vale. What is it to you?_";

begintalknode 40;
state = 31;
personality = 3;
nextstate = -1;
question = "That cough sounds pretty bad.";
text1 = "He looks annoyed. His wheezes have a distinctly tubercular sound to them.";
The first node, number 38, is the beginning of the conversation. The party initiates conversation with Avizo, starting at state -1. Then the texts (the things labeled "text1" and "text2") are displayed. At that point, the conversation moves to what's labeled "nextstate," namely state 31. Then that node is done.

Look to the next two nodes. Both have "state = 31;" in them. That means they'll be available when the nextstate from a node is 31. Node 38 contains "nextstate = 31;" so they'll be available right now. What does that mean? That means that the parts labeled "question" in nodes 39 and 40 will show up as questions that the party can ask the character. The first possible question is "Are you on a mission here?" and the second is "That cough sounds pretty bad."

So far all the player has seen are the two texts from node 38 and the two questions from 39 and 40. The player now gets to choose to ask one of the questions. If the choice is the first one, then the text from node 39 will show up. The nextstate for node 39 is 32, meaning that whatever nodes have states of 32 will become available (so their questions will show up underneath the text), too.

If the player chooses the second choice, going to node 40, the text from node 40 will be displayed, but the nextstate is -1, so no questions will be displayed. Instead, the player will be given the choice to start the conversation over (from node 38, where it began) or end dialogue altogether.

This sounds much harder than it is. Play with it just a little bit, and you'll get the hang of it pretty quickly. There are more advanced features, like condition, action, and code, but they are for later, once you've gotten the hang of the rest of this.

CONCLUSION
These are some of the basic tasks in BoA. The call message_dialog allows you to give text to the player to read. The flow controller "if" allows you to check virtually anything in the game. One-Shots allow you to do something once and never do it again. Dialogue allows you to have conversations between the party and other characters in the scenario.

A bit of advice to the new designer: of the many kinds of scripts that you can design (town, scenario, dialogue, creature, terrain, custom objects, outdoor section), the first kind of script that you should master is the town script, and you should learn how to use the information listed above. Save trying to take on terrain or creature scripts for a bit later, as they are not really necessary at first. Town scripts, however, are important, and the information above should help to get you started scripting.

And above all, be persistent. Making scenarios is tricky, but if you keep working, you (yes, even YOU) can make that scenario you've been thinking about but weren't sure you could really create.

--------------

Look! I wrote an article! What do you think? The "code" part looks horrible in the forums, but it will look better on the SW site.

EDIT 1: Changed the bit about the One-Shots. Just remembered that I've only observed the "slowing down" effect in START_STATEs. Er, wait. Maybe I haven't. Has anyone else noticed this? I have to do some experiments now. This part may be wrong.

EDIT 2: Decided to err on the safe side for the moment.

EDIT 3: Made Isaac's correction.

EDIT 4: Corrected based on Keep's comments.

[ Friday, July 30, 2004 11:38: Message edited by: Kelandon ]

--------------------
Arancaytar: Every time you ask people to compare TM and Kel, you endanger the poor, fluffy kittens.
Smoo: Get ready to face the walls!
Ephesos: In conclusion, yarr.

Kelandon's Pink and Pretty Page!!: the authorized location for all things by me
The Archive of all released BoE scenarios ever
Posts: 7968 | Registered: Saturday, February 28 2004 08:00
Warrior
Member # 286
Profile #1
Wow, that IS simplified. :) Which I guess is a good thing, in this case.

--------------------
Z: "I just feel so insignificant."
Psych: "You ARE. You're an ANT."
--Antz
Posts: 104 | Registered: Saturday, November 17 2001 08:00
Warrior
Member # 4202
Profile Homepage #2
quote:
SDFS
Stuff-Done Flags (SDFs) are the basic way of keeping track of whether something has been done or not. They are, as the docs describe, essentially a grid of numbers that is sized 30 by 300, with each number able to range between 0 and 256. The docs have a fairly good description of them, so I won't say more than that you should have some handle on what they are before continuing with this article.

That should be between 0 and 255, not 256.

--------------------
Creator of the 3D Blades of Avernum Editor for Mac. Get it at Ingenious Isaac's Illusion, my web page. Better yet, get Battle for Wesnoth, a wonderful free TBS game.
Posts: 192 | Registered: Sunday, April 4 2004 08:00
Agent
Member # 2820
Profile #3
EDIT: Uh, nevermind.

[ Monday, July 26, 2004 07:25: Message edited by: Keep ]

--------------------
Thuryl: I mean, most of us don't go around consuming our own bodily fluids, no matter how delicious they are.
====
Alorael: War and violence would end if we all had each other's babies!
====
Drakefyre: Those are hideous mangos.
Posts: 1415 | Registered: Thursday, March 27 2003 08:00
Warrior
Member # 1451
Profile Homepage #4
What about that

action = INTRO //You may not want to use this

I might have gotten that quoted wrongly, Commander Terrance has this in his dialogue. What does that mean? How is that used?

--------------------
I am pleased to make contact with your entities.
Posts: 123 | Registered: Sunday, July 7 2002 07:00
Off With Their Heads
Member # 4045
Profile Homepage #5
Er, literally straight out of the docs, 2.14: Creating Dialogue:
"INTRO: If party has already talked to a character with this personality number (or a personality number is not given), shows only text5, text6, text7, and text8. Otherwise, shows text1, text2, text3, and text4."

Basically, you use it on the first node that a character says. Write an initial description (what the party sees when they first look at the character) in text1. Write something else (usually something like "_What else do you want to know?_ he asks.") in text5, and that will show up when the party initiates conversation again or runs out of dialogue options and chooses to start the conversation over.

--------------------
Arancaytar: Every time you ask people to compare TM and Kel, you endanger the poor, fluffy kittens.
Smoo: Get ready to face the walls!
Ephesos: In conclusion, yarr.

Kelandon's Pink and Pretty Page!!: the authorized location for all things by me
The Archive of all released BoE scenarios ever
Posts: 7968 | Registered: Saturday, February 28 2004 08:00
Agent
Member # 2820
Profile #6
I think that Jeff erred on the side of using

if (condition)
end()
if (condition)
{stuff}

because that is the direct translation of "if ... then ... otherwise ..." in BoE. I E-mailed this to Jeff and he said that the code is stored in memory and the code IS NOT interpreted, just scanned through for the closing brace. While this takes up some CPU time, I'd think this would be equivalent to doing a spell check in a word processor.

As a note, don't use end() in a creature script for various reasons.

--------------------
Thuryl: I mean, most of us don't go around consuming our own bodily fluids, no matter how delicious they are.
====
Alorael: War and violence would end if we all had each other's babies!
====
Drakefyre: Those are hideous mangos.
Posts: 1415 | Registered: Thursday, March 27 2003 08:00
Off With Their Heads
Member # 4045
Profile Homepage #7
I have in one instance hidden a cut scene (literally thousands of lines of code) in a town's START_STATE, which I'd think would make a noticeable difference. Meh, now I really do have to try it. I'll report back shortly.

EDIT: Amazingly enough, it makes no noticeable difference. The two formats perform identically, even with 500 lines of code in a START_STATE. I will modify this article appropriately.

Evidently I just imagined this effect....

[ Friday, July 30, 2004 11:30: Message edited by: Kelandon ]

--------------------
Arancaytar: Every time you ask people to compare TM and Kel, you endanger the poor, fluffy kittens.
Smoo: Get ready to face the walls!
Ephesos: In conclusion, yarr.

Kelandon's Pink and Pretty Page!!: the authorized location for all things by me
The Archive of all released BoE scenarios ever
Posts: 7968 | Registered: Saturday, February 28 2004 08:00
Agent
Member # 2820
Profile #8
Interesting that you noticed this effect with simple One Shot specials.

Something that DOES slow down the engine is pathing to an impossible spot when the monster has many fake options to try. For example, pushing 3 or 4 creatures away from their starting points and locking them out will cause the game to slow down horribly.

--------------------
Thuryl: I mean, most of us don't go around consuming our own bodily fluids, no matter how delicious they are.
====
Alorael: War and violence would end if we all had each other's babies!
====
Drakefyre: Those are hideous mangos.
Posts: 1415 | Registered: Thursday, March 27 2003 08:00
Shock Trooper
Member # 3276
Profile #9
ONE-SHOT SPECIALS
I wish there was a just_once command like in Civilization II. Talk about lazy programming :) just joking.
So, this is like a 'Basic Scripting for Dummies.'?
Posts: 249 | Registered: Saturday, July 26 2003 07:00