ID:36233
 
Keywords: beginner, tutorial, zbt

Welcome to Zilal's Beginner Tutorial for the BYOND system. I wrote this because even newbies who read the guide, and look at Your First World, are often confused and maybe a little overwhelmed and still don't know where to start. And when you're confused about a new thing, you frequently don't know even enough to think up the right questions to ask to get yourself help.

I'm going to walk you through the creation of a simple world, and I'm not going to assume you know anything about programming or BYOND already. And I'm going to show you one way to do what beginning programmers often want to know how to do: use the mouse to move things around a playing field.

Below, the titles of each section are meant to be approachable; what's in parentheses after the title is a more clinical indication of what you'll learn in that section.

I. Getting Set Up (Opening the program)

Go to your program menu and open the Dream Maker program. Under the File menu, choose "New Environment...", click on the right side of the input box, and type "Testgame". Click OK. When the next box comes up, you will see it wants to create a new Code File with the same name. Click OK.

Every world has to have an environment. For one world you may have many code files, or many sound files, and so on, but they're all part of the same environment. In our case, the Testgame environment. A code file (or .dm file) is where you do the actual programming. In the panel on the left, you can see little icons representing the environment and the Testgame code file.

A folder for this environment was created in your BYOND\bin\ folder, since we didn't specify otherwise. You can go look if you want. By default, BYOND is installed in C:\Program Files\BYOND (if you don't know where BYOND was installed on your computer, it's probably there). In the bin folder, you'll see a Testgame folder. In that, you'll see Testgame.dme (the environment) and Testgame.dm (the code file).

II. First Things First (Comments)

At the top of Testgame.dm, our code file, put this line:

//This is a test game created by <your name> on <today's date>.

...where you replace what's in the >'s with the appropriate information.

That's a comment. It begins with the double-slash, which makes it turn grey. You can say anything you want in a comment and it won't affect your code. Comments are very important; a good programmer adds comments to her code explaining how it works. A comment should be made as if you're explaining the code to another programmer, even if you never intend anyone else to see it.

That may sound silly, but forgetting how your own code works is a lot easier than it sounds. It's far better to have good commenting and not need it than to need it and not have it.

III. Code that Does Stuff (Verbs)

Hit return a couple times to move down the page, and put in this code (indent lines by pressing the tab key):

mob
verb
say(msg as text)
world << "[usr]: [msg]"

We made our first verb. Verbs are the commands the player has access to in a game. We started off with "mob", which means that for the verb to work, a mob must exist in the world. Which could be you. (If we had started off with "turf", then the verb would require a turf to exist. And a mob too, in fact, since without at least one mob in the world there'd be no one to use the verb.) Mob is short for mobile and is usually used to represent players and monsters.

Under mob we put "verb". We used a tab before verb, which means it belongs to mob; mob is the parent. If we'd put "verb" all the way on the left, it wouldn't belong to anything.

Next is "say" and some stuff. By tabbing and putting it under verb, we're indicating that say is a verb. What goes on with that line is slightly more complex. Before we get to that we have to comment our code like good little programmers.

mob
verb
say(msg as text) //what the usr says is passed into "msg" as text
world << "[usr]: [msg]" //the world sees chatroom-like output

Any of the comments in this tutorial may wrap around the screen in your text editor, but there's no carriage returns in each individual one. Now I'll explain what the comments above mean.

(When you're still learning the language, comments also help to better remember what you just learned... like summarizing in your own words the chapter you're reading for History class. You'll be grateful later when I call you up to test you.)

IIIa. Arguments Are Healthy (Verb arguments)

        say(msg as text)            //what the usr says is passed into "msg" as text

As programmers put it, verbs take arguments. But that's not very helpful if you don't know what an argument is or how it can be taken. We can all see the word doesn't make much sense. What exactly are we arguing about? (I prefer to argue about Schweppes and Canada Dry, but if you were interested in that you'd read my tutorial on how to acknowledge that Canada Dry is superior.)

Think of an argument as input. With a verb, this input comes from the player. You decide what name to call the incoming information. In this case, it's "msg", short for "message." We could call it anything we want, but it's good to pick a name that's both descriptive and easy to type.

So, we don't know what input is going to come in here, but whatever it is, we know what we're going to call it. And we know what type of input it's going to be. Text. Not a number, not a mob, but text. Which is words, mostly. Text might contain digits in it: "I am 23 years old"--but that doesn't make it a number. Msg as text.

A player who types "say" in the game will then be able to type in any text he wants. The text gets passed in to the say verb. Picture a player using your verb to say something like "Hello!", and imagine those words dropping right into "msg".

IIIb. Output! (The operator)

            world << "[usr]: [msg]"     //the world sees chatroom-like output

We put this line under the verb, indented, since it's the action the verb does. Those two lesser-than signs are called an operator. "The operator," in fact. Don't ask me how to pronounce that. Operators are symbols, usually placed between things, that cause something specific to happen. In this case, what's happening is that the right side is being output to the left side. Which is the whole world, in our case. Picture the stuff on the right being funneled >> into the world. Uh, in the opposite direction as we did above. Here it is in the same direction.

.dlrow eht otni delennuf gnieb thgir eht no ffuts eht erutciP

Makes much more sense, doesn't it?

IIIc. What the Heck Is That? (Imbedded expressions)

What exactly "[usr]: [msg]" means might have you stymied at this point. It has quotes around it, which means the whole thing is a text string. What the player typed in, our msg, is a text string... I called it "text" before since, hey, that's what our verb said. Msg as text. Not msg as text string. The verb does not lie. But they do mean the same thing.

When something in a text string has [] brackets around it, that's what they call an imbedded (or embedded) expression. I don't know where programmers come up with this stuff. I believe they sit around drunk throwing darts at a page of the Oxford English Dictionary. However, the imbedded part of "imbedded expression" makes some sense. An imbedded expression is like a nugget buried in our text string.

When your code gets executed... which means run, rather than guillotined, not that that wouldn't be fun... that text string doesn't get funneled into the world exactly as you see it above. Rather, anything that's an imbedded expression gets replaced. You'll see instead whatever the word in brackets refers to. The [usr] is replaced by the usr's name. Usr, also a pronunciation challenge, is whichever player just used the verb. When a text string containing [usr] gets displayed in your world, the [usr] part gets replaced by the verb-user's name.

If I know you, you've already figured out what [msg] gets replaced by. I don't know you; however, this should not stop you from sending me money. Think fast! If a player named Ichabod uses the say verb to say "I'm going to whack everybody with a 30-pound petrified hunk of mutton!", what will the world see?

Yes, that's right. Stars. No, no, I jest. The world will see...

Ichabod: I'm going to whack everybody with a 30-pound petrified hunk of mutton!

Voila. Chatroom. (You know it's a chatroom because the person speaking already shows a baseless hostility toward everybody else.)

By the way... never let an expression im bed with you. They steal the covers.

IIId. Action! (Compiling and running)

Go under the Build menu and choose "Compile." In the bottom panel you should see:

loading Testgame.dme
saving Testgame.dmb

Testgame.dmb - 0 errors, 0 warnings

If you don't, check your code to make sure it looks like what's above.

When your code is compiled, it's translated into a format the computer (and/or whichever program is running your program) can read. If there are no errors during this process, your .dm code file will also be saved. Now go under the Build menu again and choose "Run." The Dream Seeker (a client, or interface to other programs) will open.

If you're prompted by the Dream Seeker to choose a key, go ahead and do that.

Once your world opens you should see the verb "say" in a panel named "Commands." Click on that or type "say" on the command line (the pink type-in window), then your message. If you're using the command line be sure to put a space in between. And press enter. Now get back here!

IV. This World is Pretty Friggin' Boring (Icons)

Picky, picky! We'll make it a little more interesting. Under the File menu in the Dream Maker, choose "New..." and when the box pops up, choose "Icon File (.dmi)". Name it "square".

Now you're in the icon editor; you can see a new screen with two buttons, a palette and a camera. Click the palette. What you see next is probably self-explanatory; flood the screen with a color you like, then draw a contrasting border around the edges.

IVa. Fancy Fancy (Icon states)

Next, make a new Icon File (.dmi) and name it "stone". Click the palette and draw a filled-in oval in a color that will show up nice on your square. If you mess up, the color in the very upper-left of the palette can be effectively used as an eraser. It's called the mask, and parts in that color will be transparent in your game.

Once you're done hit the "Back" button in the lower right. You'll see your little stone in the space that was blank before. Click the palette again. Draw another stone in a different color that would look nice on your square, and go back again.

We just created an icon with multiple states. It's still one .dmi file, one icon, but it can look two different ways. Instead of thinking of it as a painting, think of it as a picture book, and you can flip to whichever picture you want and then show it to people. The whole "book" together is named "stone.dmi". This one was NOT on the New York Times bestseller list.

Click a millimeter or two beneath the first stone. There may be a split second delay and then a little box should appear. In the box, type in the name of the color you used for that stone. Now do the same with the second stone, naming it after its own color of course. We've named our icon states. Now when we want to display a particular one we can call it by name.

This may be the only beginner's tutorial to talk about icon states. Icon states are considered a difficult concept. Many people who've been coding for months still don't understand them. If I've done my job, you're sitting there wondering what's not to understand.

If I haven't, look for my "Conquer Icon States in Just 60 Days" tutorial in the near future.

IVb. Implementing Graphics (Prototypes)

We made some (sort of) nice graphics, but they're not going to jump into the game all by themselves. Double-click on Testgame.dm, our code file. Beneath the mob code, stick this in:

obj                         //new obj prototype,
stone //a stone,
icon = 'stone.dmi' //with the 'stone.dmi' icon assigned

After that put:

turf
square //defines a "square" prototype, a kind of turf...
icon = 'square.dmi' //and has an icon named 'square.dmi'. In single quotes!

A prototype is like a blueprint. A blueprint of a house isn't the house itself... and I don't recommend using it to keep the cold out... but you do use it to build a house. Our code here doesn't create squares and stones in the game. It's just the blueprint. We can make as many squares and stones from that one blueprint as we want. Finally:

world                   //we set one of our world's characteristics:
turf = /turf/square //its default turf is the square turf.

We'll take a look at what that did in a moment. Right now, go under the Build menu and compile again, which besides translating the new code will save our files. Always remember to save regularly, and make backups (with real projects, anyway). If you ever forget, there's a 99.9% chance that'll be the day lightning sizzles out of a clear sky to strike your computer.

I had to rewrite half my original tutorial because I forgot to save, and my computer sensed this and obediently crashed. Do as I say, not as I do.

IVc. Cartography (Making a map)

Time to make a map for our world. Under the File menu, choose "New..." again and make a new "Map File (.dmp)" named "Testgame". Same name as our environment. Remember the environment?

Yeah?

So why aren't you recycling, hot shot?

You'll be asked to set the map size; just click OK. When the map editor comes up, you'll see it's all squares. I hope. That's because we set the world's default turf to square, and we set square's icon, and the map automatically filled in everything with it. That saves us the work of putting in all that squares ourselves. How devious of us.

You now have a world of 100 tiles. Compile again, and run. You'll see the squares... but you won't see any stones.

If we're going to make a strategy/board game, we'll have to tell it how exactly to set up all the stones at the beginning.

V. More Creepy Abbreviations (Procs)

Proc is short for procedure. These are little bits of code that make all the action happen in the game; you can recognize a proc because parentheses come after its name. Verbs are a type of proc, so you made one earlier without even knowing it. The DM language comes with some built-in procs to make coding easier. Since DM is also about power, we're allowed to override the built-in ones and make them do exactly what we want.

Va. New() (Built-in procs)

When your program is ordered to go do a procedure, the relevant proc is "called," as they say. An object's New() proc gets called automatically whenever a new instance of that object is created. Normally the New() proc doesn't do too much, but we're going to override the world's New() proc so that it does something whenever the game is opened up... in other words, whenever a new world is created. Here's our new world code:

world                   //we set one of our world's characteristics:
turf = /turf/square //its default turf is the square turf.
New() //overrides its New() proc
for (var/turf/square/T in world) //loops through all squares,
new /obj/stone(T) //putting a stone on each

..() //calls the parent

You've been a very fidgety student and I have considered calling your parent, but that's not what the comment is about. The ..() command says to go call the actual built-in New() proc. This is not necessary when overriding every built-in proc, but with some it is very, very important. Many built-in procs perform heavy-duty functions; they're also called parent procs because they're what new versions are derived from. (Didn't you ever ask, "Mom, how was I derived?")

The New() WE coded is a child, and it doesn't know everything the parent does. So we tell it to go ask its parent what to do with that ..() line. That assures that New() will do what we told it plus what it was built to do in the first place

As for the other code, it's nice and simple. We use var/turf/square/T as a temporary variable to hold any turf we want. When we say for (var/turf/square/T in world), we tell the program to "loop" through all the squares in the world, and temporarily drop each one into the T variable so we can do something to it. And we do. For each square, we create a new /obj/stone prototype, with (T) in the parentheses to indicate the new stone should be put there. Then the for loop continues and puts a new square in the T variable, till it's done them all.

Now, compile and run. Where are our stones??

Vb. Getting picky (Non-overridable built-in procs)

They're not there, because our program got confused. We gave 'stone.dmi' two icon states, and it doesn't know which one to pick. So we're going to tell it. In fact, we're going to make it choose right when stones are created. You know how to do that, right? Put the code in the stones' New() proc!

obj                         //new obj prototype,
stone //a stone,
icon = 'stone.dmi' //with the 'stone.dmi' icon assigned

New() //overrides its New() proc
icon_state = pick("<color1>","<color2>")//set each stone's icon state randomly

...where "" and "" are the colors of your stones. If your stones are red and blue, for instance, the line should read "icon_state = rand("red","blue")".

The proc pick() is also built-in, but it doesn't belong to anything. It's only called when we explicitly call it. That's the case with all lowercase built-in procs. (You can find more in the DM Reference.) Each is provided to perform a useful function that would be annoying to have to code ourselves. In this case, the proc randomly picks one of the things we put in the parentheses.

You now have a chatroom in which stones get set up in a random pattern each time the game is loaded. Ain't life grand? You should Save All or Compile again now, by the way. And run, if you like!

VI. But I Don't Want a Chatroom!! (Variable declaration)

I know, I know. You want an exciting strategy game, not a lounge with ugly tables. I think it's time we learned to move these stones around with our mouse. Or mice, as the case may be. But before we can pick up stones, we're going to need a place to put them while they're not on the board.

The characteristics of mobs and turfs we've been fooling around with (such as "icon" and "icon_state") are called built-in variables. A variable is a place where a tidbit of data is stored. It's called a variable because we can change it at will, so the info it holds can vary. The DM Reference lists which variables are built-in and already there for you to use.

But we want to make a variable for our own special purposes. A variable to hold the stones we will learn how to pick up, so they don't disappear forever once we take them off the board. Make an addition to your mob code, below "mob" and above "verb":

mob
var/obj/stone/holding //a variable to keep track of held stones

We just defined a new mob variable. It's similar to what we did with var/turf/stone/T, except this one is permanent, so to speak; it's not inside any proc, so it won't go poof when the proc finishes its work. It belongs to mob and it'll be available to us anytime, anyplace. The way we declared it indicates it's meant to hold a reference to not just anything, but instances of the obj/stone prototype.

VIa. Pickin' 'em up... (More built-in procs; src)

If we're going to go around picking up stones, we really ought to code that in now. I mean, honestly. Add this before our obj/stone's New() proc:

    Click()                     //overrides its Click() proc
loc = null //takes the clicked stone off the board
usr.holding = src //but "remebers" it
..() //calls the parent, just in case

Click() is another built-in overridable proc, like New(). The built-in version doesn't do anything at all, I can tell you that right now. It's worse than your no-good lazy Uncle Owen. But it will be called automatically whenever an obj/stone is clicked, so we will make use of it.

The first thing we have the Click() proc do is set the clicked stone's location, or loc, to null. (Null can mean a few things, but for our purposes today, it means "nowhere.") Our poor stone has been banished from this mortal plane. But wait! We will keep track of it by assigning it to the usr's holding variable. The usr is whoever clicked his mouse on the stone; src means the source of the proc, which is the stone.

Compile again. Wouldn't want to lose your precious code!

VIb. Five Notes (Clarification, advice, info, warning)

1. I've put comments on almost every line in our Testgame program, but that's neither expected nor desirable in normal practice. Just put in enough commenting that another coder reading your stuff would be able to figure out basically what it does. Or exactly what it does, perhaps, with very complex code.

2. Note how we give our variables names that relate to their purpose. I strongly recommend this. Make a variable's name as descriptive as you can without making it a pain to type repeatedly.

3. When we declare a variable from within a proc, it exists only in that proc, as I mentioned. Once the proc returns the variable goes poof. If you want another proc to use that variable you must pass it in. You'll learn more about that as you progress in your study of BYOND.

4. If you recall, we have a line saying "icon_state = pick("lt;color1>","")" inside obj/stone's New() proc. If you took that out of the New() proc and put it right below the stone's "icon = 'stone.dmi'" line, you'd get an error:

Testgame.dm:13:error:= :expected a constant expression

That's because if something is unknown when you compile... such as what a random choice will turn out to be... you have to put it inside a proc.

5. I've had you call the parent of both built-in procs we overrode, Click() and New(), while stating that it isn't always necessary. It isn't, but it also doesn't hurt -- unless you don't want the parent to perform its usual function, but that's rare; when you don't want a proc to do its usual thing, you'll know it. The idea is that even when a particular coding convention isn't always necessary, it's best to make a habit of doing it all the time... like commenting. Because if you don't get in the habit, chances are, the times you'll really need it will be the times you'll neglect to do it. Good sturdy coding habits may take more time in the short-term, but in the long-term they can save you hours of frantically searching for what just went wrong.

VIc. Puttin' 'em down (The if and else statements)

If you run your game now, you'll see that you can indeed pick up stones by clicking on them... that is, assuming you coded exactly what I told you. The problem is that you can't put any stones down. Let's learn how to do that now. Here's our new obj/stone Click() proc:

    Click()                     //overrides its Click() proc
if (usr.holding)
usr.holding.loc = loc //puts held stone on clicked stone's square
usr.holding = null //makes it so usr is no longer "holding" anything

else
loc = null //takes the clicked stone off the board
usr.holding = src //but "remebers" it

..() //calls the parent, just in case

Now to dissect that.

        if (usr.holding)

Thanks to our well-named variable, that's pretty easy to understand: if the usr is holding a stone, execute the code in the "if block." Which is...

            usr.holding.loc = loc       //puts held stone on clicked stone's square

Here we put the held stone down where the clicked stone sits.

            usr.holding = null      //makes it so usr is no longer "holding" anything

Last in our if block, we make usr.holding equal nothing, so the program doesn't think it's still holding what we just put on the board. Very important.

        else

And as you might have figured out, this is what the program will do in the chance our if statement fails. That is, if the usr isn't holding a stone. Compile and try it out!

VId. Procadile Dundee (Proc declaration)

So now we can pick up stones and put them down. But it's not much of a game, is it. Unless our target audience is the contingent of BYOND two-year-olds, we need to spice this up!

I have an idea. Let's make it so when you put a stone somewhere, it turns all the other stones in its row and column that same color. That's not a very challenging game, so we'll make the player have to do it in seven moves or less. What fun!

All right, so it's not exactly chess. You're not exactly Gary Kasparov.

The first part of designing a game is figuring out what you're going to need in terms of procs and variables. What are the tasks your game will perform? Are there built-in procs and variables for these tasks, or do you need to create your own? You have a head start today; I already gave you some of what you need. But now we've set some game rules and conditions, and we'll need even more variables and procs to keep track of those. What rules did we set?

(A) Putting down a stone will make stones in its row and column the same color, and

(B) The player has to do this in seven or fewer moves.

So what do we need? I'll tell you what we need. A proc to change stones' color, one to check if all the stones are the same color, a variable to keep track of the move number, and another proc to see if the maximum move number has been exceeded. Let's make those. First, the easy one:

mob
var/moves //tracks player moves

In keeping with our alphabetical order within similar code, we put that above the var/obj/stone/holding variable. We just need to make sure the move number gets increased when it's supposed to. That's when a player puts a stone down. So above the << output to the usr, put:

            usr.moves++             //increments usr's move number

Like the comment hints, the ++ means "increment," and it increases the variable by one. Mob/moves is nothing to start with, and nothing plus one is one, so this'll work out just dandy. Since the moves variable was declared outside a proc, it'll even remember whatever we do to it. Next I think we'll work on changing and checking stone color. We're going to have to make brand new procs for that. Here's one way to do it:

proc
ColorChange(var/obj/stone/thestone) //takes a stone as an argument
for (var/obj/stone/O in world) //loops through all stones
if (O.x == thestone.x || O.y == thestone.y)
O.icon_state = thestone.icon_state //changes those in same row or column

return ColorCheck(thestone.icon_state) //returns whatever ColorCheck() returns

ColorCheck(color) //takes a color as an argument
for (var/obj/stone/O in world) //loops through all stones
if (O.icon_state != color) return 0 //returns 0 if one is a different color

return 1 //returns 1 if the game is won

These are very short procs, but there are some new and complex things going on in them. Let's take a look.

proc
ColorChange(var/obj/stone/thestone) //takes a stone as an argument

These procs won't belong to anything, so we indent them under the word "proc", which indicates they're all alone. Remember mob/verb/say(msg as text)? That was a verb that took an argument, or input from an outside source. In that case the input was text, and the outside source was the usr. This time, the input will be a reference to... you guessed it... a stone. We're going to call it "thestone" to keep track of it, and when we call this proc, we'll have to be sure and remember to "pass it in."

        for (var/obj/stone/O in world)              //loops through all stones
if (O.x == thestone.x || O.y == thestone.y)
O.icon_state = thestone.icon_state //changes those in same row or column

You've seen the looping thing before. But the next line may be a mystery. The || means "or," and the double-equals, ==, is what you use when you're not making something equal something else, only checking if they do. What the line is doing is taking a look at, or evaluating, each statement on either side of the || and determining if either one is true. If that's so, the if block gets executed.

O.x and thestone.x refer to their x coordinates, and y of course refers to their you-know-what. The proc looks at each stone in the world and if their x or y is the same as the x or y of the stone that was passed into the proc, its icon_state is made the same as that stone. (Since the icon_state variable effectively holds the stones' color, we won't need to make a separate "color" variable for the stones.)

        return ColorCheck(thestone.icon_state)          //returns whatever ColorCheck() returns

Right after we've changed the stones' color is when we want to check if they're all looking the same. So we call the ColorCheck() proc. In a new and exciting way. We are going to get whatever the result of the ColorCheck() proc is and there at the end of ColorChange(), return it to ColorChange()'s caller proc. For convenience, we pass in the color that stones were just being changed to... that way we won't have to guess what color the player is trying to turn all the stones.

    ColorCheck(color)                       //takes a color as an argument
for (var/obj/stone/O in world) //loops through all stones
if (O.icon_state != color) return //returns if one is a different color

return 1 //returns 1 if the game is won

Here's the other proc. While we could have stuck its code into the end of the first proc, it's good to separate different tasks into different procs. Nice, compartmentalized code is easier to work with.

We do that old familiar looping here, this time checking to see if the next stone up is a different color than the color that got passed in; != means "does not equal." If O.icon_state checks out to be different than the color we're looking for, we return the proc. That sends execution of the program back to the ColorChange() proc (which promptly ends, having nothing left to do). But if the ColorCheck() proc manages to loop through every stone in the world without getting to one that's a different color, it won't return in the if block. It will make it all the way down to the line that returns 1.

The alternative is to loop through every stone, tally the differently colored, and then when the loop's all over check and see if any odd stones were recorded in the tally. But we're better than that. It's a nice shortcut to have a proc return as soon as it can tell the conditions aren't met.

Now we just need a proc to check if the maximum move number, seven, has been exceeded. You can put this below the ColorCheck() proc:

    MoveCheck()
if (usr.moves == 7)
usr << "You've used up all your moves. Game over!"

else
usr << "You have [7-usr.moves] move\s remaining."

I didn't comment this one since by now we're getting pretty good. I think you got it figured out. Just a little math there, right in the imbedded expression. Yep, you can do that. And oh, you see that backslash? That's the \s text macro. It looks at the last imbedded expression and, if it was plural, adds an s to "move." None of that "1 moves remaining" stuff for you! Your code will be smooooth.

But we've just made three brand new procs... and only called one of them, ColorCheck(), in our code. If we want to use the others we've got to put in code to call them too. How about MoveCheck(), when would be a good time to check whether the player's used up all his moves? After an unsuccessful move, of course. Well how do we know if a move was successful? We call the ColorChange() proc, which calls the ColorCheck() proc! Then we'll see. Our new Click() proc is:

    Click()                     //overrides its Click() proc
if (usr.holding)
usr.holding.loc = loc //puts held stone on clicked stone's square
usr.moves++ //increments usr's move number
if (ColorChange(usr.holding)) //if ColorCheck() returns 1 to ColorChange()
usr << "You've won the game in [usr.moves] moves!"

else
MoveCheck() //if the game wasn't won, see if it's been lost

usr.holding = null //makes it so usr is no longer "holding" anything

else
loc = null //takes the clicked stone off the board
usr.holding = src //but "remebers" it

..() //calls the parent, just in case

See our new if/else? In that if statement, we check to see what ColorChange() returns, which -- since ColorChange() returns the result of ColorCheck() -- is 1 if the game is won, and 0 if it's not. We use a little shorthand there; note we didn't say "if (ColorChange(usr.holding) == 1). When there's no ==, or !=, or >, or anything like that inside an if statement's parentheses, the program just checks to see if what's in there exists. And it's not checking to see if the ColorChange() proc exists... but whether what it returns exists. That is important. If the ColorChange() proc returns 1, well, 1 exists. If it returns 0... 0 is nothing, so 0 doesn't exist. A statement of "if (1)" will evaluate as true, and "if (0)" as false. We're just being sneaky about it.

We also passed the usr's held stone into ColorChange(), just like we were supposed to. This is the only time in your life you will experience the passing of a stone painlessly.

The other change is the new else statement, which calls Movecheck() if the game hasn't been won. See how the new if and else reside within the original if block? You can call that "recursive," or "nesting." Aww, how cute. When nesting, make absolutely sure you have things indented where they're supposed to be. An else must always be at the same indentation level as its partner if.

With the appropriate things in the >'s, now all our code together should look like:

//This is a test game created by <your name> on <date>.

mob
var/moves //tracks player moves
var/obj/stone/holding //a variable to keep track of held stones
verb
say(msg as text) //what the usr says is passed into "msg" as text
world << "[usr]: [msg]" //the world sees chatroom-like output


obj //new obj prototype,
stone //a stone,
icon = 'stone.dmi' //with the 'stone.dmi' icon assigned

Click() //overrides its Click() proc
if (usr.holding)
usr.holding.loc = loc //puts held stone on clicked stone's square
usr.moves++ //increments usr's move number
if (ColorChange(usr.holding)) //if ColorCheck() returns 1 to ColorChange()
usr << "You've won the game in [usr.moves] moves!"

else
MoveCheck() //if the game wasn't won, see if it's been lost

usr.holding = null //makes it so usr is no longer "holding" anything

else
loc = null //takes the clicked stone off the board
usr.holding = src //but "remebers" it

..() //calls the parent, just in case

New() //overrides its New() proc
icon_state = pick("<color1>","<color2>")//set each stone's icon state randomly

turf
square //defines a "square" prototype, a kind of turf...
icon = 'square.dmi' //and has an icon named 'square.dmi'. In single quotes!

world //we set one of our world's characteristics:
turf = /turf/square //its default turf is the square turf.
New() //overrides its New() proc
for (var/turf/square/T in world) //loops through all squares,
new /obj/stone(T) //putting a stone on each

..() //calls the parent

proc
ColorChange(var/obj/stone/thestone) //takes a stone as an argument
for (var/obj/stone/O in world) //loops through all stones
if (O.x == thestone.x || O.y == thestone.y)
O.icon_state = thestone.icon_state //changes those in same row or column

return ColorCheck(thestone.icon_state) //calls the color checking proc

ColorCheck(color) //takes a color as an argument
for (var/obj/stone/O in world) //loops through all stones
if (O.icon_state != color) return 0 //returns 0 if one is a different color

return 1 //returns 1 if the game is won

MoveCheck()
if (usr.moves == 7)
usr << "You've used up all your moves. Game over!"

else
usr << "You have [7-usr.moves] move\s remaining."

At this point, Save All or Compile again.

VIe. I'm a Progammer! (Anticipating problems)

Not so fast, Jack. A programmer... a good one, anyway... has to think ahead. This means anticipating what your players are going to do. Players are nasty crafty things, and if there's a problem with your game they will find it eventually. Can you spot the problem in the code we just wrote?

Yes? No? Remember that players don't see the game the way you do. You know how things are supposed to work; they don't. You are on the straight and narrow path of "supposed to," but they don't see the path that's clear to you. They wander around and run into loopholes and bugs. Or they deliberately set out to find them. You didn't realize that stuff was out there. You were on the path.

I think we should see our bug in action. Go ahead and play our clever little game. Wait until you've won or lost... then keep playing. Oops! We need to make the game stop when it's over!

VIf. Buntime Rugs (Runtime bugs)

What we have is a runtime bug. Our code all checks out, looks great... but it doesn't do what we want it to. This can be very frustrating, and you'll no doubt bang your head on the keyboard countless times over this during your programming career. This is why I have POIUYTREWQ imprinted across my forehead. In case you've ever wondered.

When faced with a problem, we (1) think of all the solutions and (2) choose the most efficient. That "think of all the solutions" part is probably a little daunting for the beginner to DM. So I want you to forget about code and just think real-world logic. This is probably a question you ask all the time in everyday life: How do I keep people from winning games they've already won?

I can think of two ways to do that: checking to see if the game is over whenever the player tries to move, and simply taking away all their stones. Which one you would choose depends on your own preferences. Do you want people to be able to show off the board once they've finished? Maybe take a screenshot? Or are you getting sick of this tutorial and just want to end their fun the cheap and easy way?

Mm-hmmm...

VIg. Cheap and Easy (The del proc)

Let's get rid of all the stones on the board once the game's over. For that, we'll make a new proc:

    CleanUp()
for (var/obj/stone/O in world) //loops through all stones
del (O) //deletes each

That comes before ColorChange() in the alphabet, so put it above. Now we need to call it when the player has won or lost. The code for wins and losses happens to be in two places, so we'll need to call CleanUp() in two places:

                usr << "You've won the game in [usr.moves] moves!"
Cleanup()

and

            usr << "You've used up all your moves. Game over!"
Cleanup()

Now try your lovely new game!

VII. AHA! (Compile-time errors)

If you typed in exactly what I showed you, you should have seen something like this when you went to compile:

loading Testgame.dme
Testgame.dm:21:error:Cleanup:undefined proc
Testgame.dm:70:error:Cleanup:undefined proc

Testgame.dmb - 2 errors, 0 warnings (double-click on an error to jump to it)

What happened? Ah, we misspelled CleanUp both times we called the proc. DM is a case-sensitive language. Get in there and clean up your u's, soldier!

Compile and test. And we're done.

VIII. Exercises

Here are a few things to try on your own. Can you...

1. Output instructions when a player clicks on a stone?

2. Re-set the board when the game is over?

3. Allow stones to be placed only on different-colored stones?

4. Look up mouse_opacity, and use it to make your game smoother?

5. Figure out why it can look like no stone was picked up, and think of more than one way to fix it?

IX. Final Words

Start small. Get comfortable with the language before you embark on that 10,000 room RPG you've always dreamed about making. Download and fiddle with the "Your First World" demo from the Tutorials section of the website.

The DM Reference I've mentioned a few times in this tutorial can be found in C:/Program Files/BYOND/help/ref, or on the BYOND website. When you have a question about how to do something, I advise scanning through the procs there to see if anything does what you want. There are a lot of them but chances are even if you don't find what you want, something else you'll find useful will catch your eye.

If you're still lost after you've thought your problem through, experimented, and scanned the Reference, check out the BYOND Forum. Browse or do searches for your issue. If you're still not getting anywhere, post to the Newbie Central board and ask your question. Be specific or it's very difficult for others to help you, and don't ask others to do your work for you.

Wrong: "How do I make a tic-tac-toe game?"
Right: "What's a good way to check for three of the same tiles in a row?"

Wrong: "Why'd I get this error?"
Right: "Here's my code for army clashes, and the army variables. How come I got this error for the third line from the bottom?"

Wrong: "Can somebody give me code for an email-based game?"
Right: "Which procs would be useful for playing by email?"

Wrong: "How do I use BYOND?"
Right: "I read the tutorials, scanned the Reference, bought the book, now all I want to know is: where can I donate money to Dantom International?"

X. There is no X. Don't ask me Y.

Z

"X. There is no X. Don't ask me Y.

Z"


I lol'd, then yea'd.
Great Article!
usr.holding = src //but "remebers" it


Remembers? Typo...