ID:40665
 
Kunark's DM Programming Tutorial


Kunark's Programming Tutorial is copyrighted 2005 by Kunark. All rights reserved.


Table of Contents:
I. DM is an "Object-Oriented Language"
II. Definitions and understanding that oldbie-foo
III. The truth about vague variables and type paths
IV. Making smart decisions (Well, decision structures, anyways...)
V. Procs, arguments, spawn()s, and returns
VI. Conquering loops (for(), while(), do while(), and goto)
VII. The benefits of sleep()
VIII. Text manipulation
IX. Using lists and more about for() loops
X. List associations
XI. Conquering the multi-dimensional list
XII: Avoiding data pile-up
XIII: Using server-side and client-side savefiles
XIV: A few miscellaneous tips

There are a few things in DM that, after learning them, provide a real step up in programming skill. They are sometimes difficult subjects, and most tutorials do not teach them thoroughly enough; however, they are some of the most important subjects in DM (and many other languages) and should be taught better. Some of them will help you in just about all areas of DM programming. Newbie, midbie, or oldbie, many people can benefit from knowing this information.

This tutorial is geared mostly towards midbies. Instead of teaching by using programming to accomplish a specific goal, it will teach about the underlying topics. It will also provide formats, examples, and problems to help teach you. If you have never programmed before, though, I would suggest learning the basics and then reading this tutorial. It is easier to learn BETTER methods of doing things once you have learned the methods that don't work as well. I also won't be spoon-feeding the newbies TOO much, so make sure you at least know some things about DM. I suggest Zilal's tutorials for the first-timer.

Chapter II (Definitions) is very important to read. If you are going to pick through the chapters and only read some of them, I suggest you definitely read Chapter II, because I will be referring to the terms defined in it throughout the entire tutorial.


I. DM is an "Object-Oriented Language"

The first step in learning DM is to learn about the object hierarchy, which is responsible for the "tree house" look of DM code, in which indentation serves to group related blocks of code together. Object-oriented languages like DM are nice because, in the hands of a competent programmer, their code is much more orderly and easily maintained than traditional "procedural" code. There is more to these structures than meets the eye, however.

There are 9 main types of data structures in DM:

/datum

Datums should be used when an object doesn't need to be visually represented on the map. They are typically used for processing information that is used by other objects. You could use /objs or /atoms instead of these, but doing so would add to the total world objects (I will explain later in this tutorial why that is bad), and each obj would hold data it wouldn't need.

For example, say you had an object structure that looks like this:

obj/InfoStoringObject
var/list/OtherPeople = list()
var/mob/TopPlayer
var/TopPlayerScore = 0

Being defined as /obj, this object structure -- that is used for the sole purpose of keeping a high score -- acquires many built-in variables of the /obj type (for example, the x, y, z coordinate variables). This adds all of those unused variables to the total world variables, and therefore risks hurting the performance of your Net Dream. The high score tracker should have been defined as a /datum/InfoStoringObject.

Avoid defining variables in the base /datum type, or else all other types of object structures will inherit that variable, which is almost never needed.


/datum/atom

Atoms all have the variables appropriate to objects that can be displayed on the map -- "icon_state", "icon", "name", "text", etc. -- so when you are accessing a type of an atom and aren't sure what kind of an object it is, you can be sure that if it is an atom, it will have one of those variables within it, and other variables you've defined as belonging to the /atom type.

Example:
atom/var/Text_Indicator = ""

proc/GetText_Indicator()
for(var/atom/a in world)
world << a.Text_Indicator

The first line adds a new variable to the definition of the /atom type. The proc displays all Text_Indicator variables for all existing objects that are based on the atom type (and its subtypes).


/datum/area

Areas are included on all tiles of the map, even if nothing else is on the tile. The default is /area. Areas can be used to section off portions of the map to give them a certain component. An example would be walking into a certain room and having it play a certain music whenever you enter that room.


/datum/atom/turf

Turfs are strictly environmental data. They are very inflexible, but their benefit is that you can have way over the usual limit of them in your world.

They are inflexible in that they cannot move around the map, and you cannot have more than one turf on a single tile. It may seem like you can, because all turfs can be stacked in the map editor; but once you run the world, those turfs are combined into a single turf (the lower turfs become mere graphical underlays of the top turf). Any turfs created at runtime will not be naturally combined in this manner -- you will have to add any desired underlays explicitly.

Try to make sure all of your floors, walls, doors, and all other map features that never have to move after they're created, are /turfs. If it is created at runtime, keep in mind that if there is already a turf in that location, the new turf will not only replace the old turf but erase its image as well.

Since you will generally have so many /turfs in your world, think carefully before defining any variables on the base /turf type, or else you may have 100,000 turfs with a useless variable on each. (However, DM does some economizing in this case, and doesn't require new memory for the new variable in a given turf until you change its value from the default. So, if create an "elevation" var with a default value of 1, and you set its value to 2 in 1,000 turfs, you're only using new memory for those 1,000 turfs that deviate from the norm.)

/datum/atom/movable

Movable atoms are /objs and /mobs. /turfs and /areas cannot be moved around like mobs and objs can.


/datum/atom/movable/obj

/objs are essentially the same as /mobs, but hold no client data (you can't connect to an /obj). Objects can be moved around on the map, unlike turfs. They can also layer on top of each other.

Items aren't the only thing that a /obj would be. You can also use them as levers, projectiles, even NPCs.



/datum/atom/movable/mob

mobs are like objs, but can be connected to by clients. In many games a player will have one mob at the beginning of the game and stay with it throughout the game, but don't be fooled -- the client actually represents the player. It's possible for multiple players to control a given mob (though only one at any given time). Think of it this way: People connecting to your game are clients. Clients are like individual gods that each control a different mob. Mobs can either be actual characters in the world, or can be used for allowing the client to see the screen such as in a board game (in which case players would be given invisible, immobile mobs that simply keep the map focused on the game board).

If you plan on having a lot of /objs in your world, such as in an RPG, you should make any sort of living thing (players, monsters, etc.) types of mobs. For something like a board game, it would be fine to just make every piece on the board a /obj and make there only be 1 mob for each player connected, used for displaying the screen to the client.


/client

This is your players. When a player logs in, a client is automatically created. When a player logs out, the client is automatically destroyed. You CAN define things on this object type, such as things you only want players to have (i.e., how many times a player has pushed an arrow key, which could be used for movement delays and such). Note that accidental disconnections, due to network problems or the like, are not usually fatal in BYOND. This is because when a client connects to a mob, the mob retains the keyname of its client -- so when a player reconnects, BYOND will automatically associate the new client with the appropriate mob. (Of course, the programmer can override this default behavior as needed.)


/world

The single /world object cannot have any custom variables added to it directly. I will explain this later more in detail, but to define global variables or procs, just use /var or /proc at the top (leftmost) level of indentation.

The world object's vars and procs affect things such as its connection to the hub, the world's name, and the default types used in various contexts (e.g., the default turf and area to use in the map editor, or the mob type that's created automatically for a new player).


II. Definitions and understanding that oldbie-foo

Something that often messes newbies up is that they don't understand the seemingly foreign language the oldbies have learned from the DM Guide. This chapter will help these newbies learn what some of those nasty programming terms mean.


Object vs. /obj: These two terms get confused all of the time. If you want to get far in DM, remember that an object is referring to a data structure, and can mean just about anything in this "Object-oriented language." An /obj, something commonly used for items in games, is just specific one type of Object, or "data structure."

Data Structure: This is commonly referred to as an "object." It holds both raw data and lists of instructions for processing that data. Part of the ease of using DM comes from its many built-in data types.

Decision Structures: Decision structures use if and else statements to determine what should be done next in an algorithm.

Conditionals: Term for statements that only process if an expression evaluates to "true" (any non-zero result is true in DM). "If" and "else" are conditionals; loops such as "for" and "while" also use conditionals.

Loop Structures: Loop structures repeat a certain set of commands until a condition is satisfied.

Resources (DM) and .rsc: Resources are every non-code component used by the executable. This means icons, maps, sounds, and just about everything other than your .dm files. .rsc files are the compiled resource caches included when you download games.

procs (Procedures): Procs are lists of directions that the compiler can understand. They can't be changed at runtime, although you can make your own compiler-like program to process your own scripting language.

verbs/commands: Verbs are a special kind of proc that can be accessible to players during the game.

Statpanels: Statpanels are created by defining them with the mob/Stat() proc. They can show data to the user or even allow the user to interact with their contents. They are shown as tabs located either at the bottom of the main screen or to the right of the main screen.

Verb Panels/Command Panels: Verb/command panels automatically appear when the user's mob has a verb accessible to it, and appear as tabs next to the statpanel tabs.

Operator: Operators perform specific actions to the left side and/or right side of the operator. Example: var/mob/M = src. The = operator defines variables. Example: M.HP--. The -- operator in this situation takes 1 away from the value on the left hand side. Example: if(M.HP >= M.Max_HP). The >= comparison operator compares the left-hand value to the right hand value; if the left-hand value is more than or equal to the right, it will give a "true" answer; otherwise, it will give a "false" answer.

Path Operators: Path operators are used to access values in a specific path. M.HP would access M's HP variable, for example.

Parameters: The values sent to a procedure when it is called. There is a chapter covering this topic later in this article.

Arguments: Parameters (see above).

vars (Variables): This is where raw data is stored. They can represent numbers, text, created objects, types, lists, images, icons, and so forth.

Initialization/Initialize: This is defining a variable for the first time. Some vars won't work until they are initialized.

const (Constant): A type of variable that is read-only (it never changes). Example: var/const/varname

tmp (Temporary/temp): A type of variable that isn't normally saved when using the savefile.write() proc. However, they can be manually saved if needed. Example: var/tmp/varname

Global var: A var that can be accessed by anything at any time without the use of path operators.

num (Numbers): These can be any positive, negative, decimal, or bit value. They are simply defined as /vars.

null: When a variable has no value whatsoever, and it is not 0 or "" (empty text string), it is null.

text: These are lists of characters contained in quotes that can be used for anything from simple messages to the user to complex lists of variables and directions.

String: A text string. See above.

char (Character): This refers to a single font character. Text strings are lists of these.

Symbol: A character that isn't a letter or a number.

Macro: No matter where you see this word, it means "shortcut". \red is a shortcut to make the following text red, and you can define macros for keys on the keyboard to be shortcuts to mob verbs.

Text Manipulation: Editing the contents of a text string. Commonly uses findtext(), copytext(), and [].

icon: This refers to DM's main graphic type, the .dmi icon. This does not refer to Windows icons (though Windows icons are often the same size as BYOND icons -- 32 x 32).

images: This term is thrown around a lot, and is correct in both contexts it is used, but in DM the word "image" refers to the /image object. The image object is useful for displaying graphics that should only be visible to selected players.

savefiles: This refers to the /savefile objects DM uses. They may be saved in encrypted savefile text format or plain text format. Their extensions do not matter as far as DS is concerned, so you could have a savefile named savefile.frr and it would be treated the same.

datum, atom, turf, obj, mob, area, client, world, etc: These are all explained in chapter 1. They are all types of objects (data structures) in DM.

Definition/Defining: Definition refers to the value of a variable, and Defining refers to setting the value of a variable. Not to be confused with #define, a preprocessor command.

Parents and children: This confusing term is the best way programmers could come up with to refer to objects that are defined farther into other objects. Here is an example: You have a /obj/Weapon/Sword. The /obj/Weapon/Sword object is the child of the /obj/Weapon object type. /obj/Weapon is a child of the /obj type. For parents, it is simply the other way around: /obj/Weapon is the parent of the /obj/Weapon/Sword type.

Algorithm: A set of instructions for accomplishing a given task. In DM, programmers implement algorithms by writing "procs".

return, returning, returns, returned: If the word containing "return" has anything to do with procs in DM, it refers to the literal "return" command (even when it's internal, as you see in the DM reference), a very important command used for making the currently executing proc stop dead in its tracks and send a result to the code that called it.

list: Lists are simply collections of other variables, whether numbers, text, types, mobs, other lists, or mixed types (it's generally bad practice to mix them, however).

Array: This is a term some programming languages use for the list data type, and in general, refers to all of the elements of a list as well as the variable itself.

Element: This refers to a singular value in a list. If you create a list with "list(1,2,5,6)", then 1, 2, 5, and 6 are the elements of the list.

Contents: Refers to all of the elements of a list, but not the list variable itself.

Matrix/Matrices: No, matrix does not refer to "bad science fiction trilogy" in DM. A matrix is basically a rectangular list with both columns and rows, when normal lists (or arrays) only have columns. In other words, it is a list of lists.

Multi-dimensional/Multi-layered list: A matrix. See above.

List associations: These allow you to associate values in lists with other values. These values can be any data type that lists can take normally, except numbers, which DM treats as specific positions in the list. List associations are commonly used for indexing the elements under different terms than the exact element value. It's a way you can avoid accessing lists by element number. Example: list( "name" = "Johnny", "score" = 5000). MyList[ "name"] would then return "Johnny". Otherwise, you'd have to remember that element number 2 was the player's score, and element 1 was the player's name, and that would be very bothersome in large lists.

Index/Indices: This refers to the position of an element in a list. var/varname = MyList[2] would make varname become the second element in MyList, and its index would be 2. When working with associative lists, "index" refers to the element that can be retrieved directly. In list( "name" = "Johnny"), "name" would be the index.

Associated value: The value associated with an index. In list( "name" = "Johnny"), "name" would be the index and "Johnny" would be the associated value. Some programmers will refer to the contents of associative lists as "key/value pairs"; in our example, "name" is the key and "Johnny" is the value. (But don't confuse this meaning of "key" with a keyname, which is a player's unique identifier.)

Node: This has many meanings outside of DM, but in DM this generally refers to a specific organizational element in a program. Hierarchies of nodes are typically indented in DM code, though it's often more convenient to use a string of nodes separated by slashes.

Bit: A value that is either 0 or 1. When evaluating expressions, 0 means "false" and 1 (or any non-zero result) means "true".

Binary: A numbering system in which the places are based on powers of two instead of powers of ten. The computer's processor handles all information as sequences of binary numbers.

Bit flag: A bit assigned a special meaning in a binary number. DM uses bit flags to determine what players can see, and for various other purposes. Bit flags can help conserve memory and speed program execution, but it's often more practical (and readable) to use individual vars instead of bit flags.

Base: A system of writing numbers. The Base indicates the number of digits that are available. Base 10 is what society normally uses (with ten digits, 0-9). In base 8, for example, the numbers 0 to 7 are written the same way as in base 10, but 8 is written as 10 (1 0). Instead of a ones place, a tens place, a hundreds place, and so forth, base 8 has a ones place, an eights place, and a sixty-fours place.

Boolean: A value that is either true or false. In DM, 0 means false and 1 means true.

ASCII: Standard numerical values corresponding with all characters and symbols. Example: The character, "A" is 65 in ASCII code.

Compile/Compilation: This is the transformation from DM code to an executable file. You cannot run a game without compiling it beforehand.

Directive: Refers to commands given to the preprocessor. Directives always start with the "#" symbol.

Preprocessor: During DM's compilation, the preprocessor uses a programmer's preprocessor directives (if the programmer has included any) to change how the code will "look" to the compiler. Directives can be used for many things, such as creating abbreviations for commonly used code, and suppressing blocks of code that aren't needed for testing.

Syntax: The grammar rules that dictate what instructions are comprehensible to the compiler. An instruction typed in a way that the compiler can't understand it will create a syntax error during compilation.

Garbage Collector: This is the internal process that maintains efficient memory use by deleting values that are no longer being used in a game. This also makes "soft deletion" possible.

CPU (DM): The CPU is the computer's processor. The world.cpu variable shows how much of the CPU's resources are being consumed by the current program (a value of 100 indicates the processor is "maxed out").

Lag: The delay between information being sent and received between the host and other clients. Slower connections such as 56k will have a lot of "lag".

Tick: Everything that happens in a DM program happens during a specific "tick" of the game clock. In DM, the default tick is 1/10th of a second. (A mob that moves 1 turf per tick can move 10 turfs in one second -- most games won't require such fast-moving mobs.) sleep(), spawn(), and animations work in sync with the world's tick.

Cache: A collection of specific resources or values (i.e., an icon cache, or a player name cache).

src: The object that owns the currently running proc.

usr: The object that initiated the current sequence of events. This usually means a player's mob, but in some contexts usr is meaningless. When a player uses a verb, the player's mob is usr -- but if you plan to call other procs from within the verb, be sure to copy the value of usr and pass it to any procs that need to know it.

Client: A client is the program that is logged into the game. BYOND's client is Dream Seeker. A client is required to play games. In DM code, the /client object represents the player's connection to the game.

Heads-Up Display (HUD): This is an on-screen display of information the player needs to play the game. This can be controls, stats, meters, panels, instructions, titles, whatever.

User Interface (UI): The way the user interacts with the game. This includes what the player has to do to make things happen (e.g., do they have to click the door or bump into it?), and what input options are available at any given time.

key: The "screen name" that you must have to login to games. It is used to represent you in the BYOND community.

The Hub: Refers to the games section of the BYOND website.

Game Hub: Refers to the specific area of the BYOND website dedicated to a specific game.

rgb: short for "Red, Green, Blue". Graphics on a computer monitor are displayed by combining various intensities of these three colors.

loc (Location): The turf a movable object is located on. Despite what many newbies think, the location() proc does not return numerical coordinates -- it returns a turf (and you can then check the turf's x, y, and z values).

Log: Some games keeps logs (in text files) of the chats that go on in a game. DM automatically keeps a log of things output to world.log as well.

Encryption/Decryption: When something is encrypted, it is converted to a special code that is unreadable to people unless they convert it back using decryption. Games often use encryption on savefiles to make them safe from cheaters who like to edit them. The program then internally decrypts the files back into a readable form.

Host: The computer that the game is running on.

Port: A port is a virtual "gateway" on your computer that can be used to maintain an active connection between two computers. Hosts can define this for customization in the links that people connect to.

Runtime: During the running of the program.

Compile-time: During the compilation of the code.

Programming Robustly: This refers to using sound programming techniques to reduce the likelihood of problems in the future.



III. The truth about vague variables and type paths


Vague variables and type paths can plague projects, making for inflexible code. Many people believe that if something is working for them at that moment in time, it must be the right approach. They need to be thinking deeper than that.

What is a vague type path?

A vague type path is when you jump from grandparent to child. In other words, the type path skips an important type that can make life easier for you and could one day reduce your game's processing time.


Examples of vague type paths:

obj/Sword

obj/HPPotion

obj/MPPotion

turf/Grass1


If you had the examples above, if you wanted to go through all of the potions in the world, you'd have to either loop through all /objs and check if they were of /obj/HPPotion or /obj/MPPotion type, or go through two loops.


Examples of specific type paths:

obj/Weapon/Sword

obj/Potion/HPPotion

obj/Potion/MPPotion

turf/Grasses/Grass1


This way, you can loop through the "/obj/Potion"s of the world for one loop that gathers all potions.

More advantages:

Say you needed the variable "StatGain" for potions. If you had that variable defined on /obj, all /objs would have that variable when only a few needed it, adding to var build-up. If you had StatGain defined on both /obj/HPPotion and /obj/MPPotion separately, then when you have to access that variable, if you were looping through a list of /objs (for(var/obj/O in oview(3,src)) and needed to access the StatGain variable, the compiler wouldn't know what you were talking about because /obj doesn't have the variable. You'd have to either use the : operator (which is almost never good practice) or check the obj's type and then assign it to a more precisely defined variable (which is more processing than should be needed, slowing your game down).

If you use specific type paths, you can declare a variable that all potions have in common, while nothing else has that variable:

obj/Potion
icon = 'Potions.dmi'
var/StatGain = 0

HPPotion
icon_state = "HPPotion"

MPPotion
icon_state = "MPPotion"

When a data structure type is only used for two types of things, sometimes it's all right simply to define it twice:

mob/Player
mob/Monster

We don't need to define anything in the middle because throughout the entire game, mobs will only be used as either players or monsters.



What is a vague variable?

In proc arguments, you'll be required to name variables and you'll have the option to define their object types. A vague variable is a variable that isn't defined as precisely as it should be.

Examples of vague variables:
var/mob/Player/movementceased = 0

proc/Proc1()
var/mob/M = new
//Now if we wanted a player-specific variable, we wouldn't be able to access it without using :
if(M:movementceased)

proc/Proc1(mob/M)
//Now if we wanted a player-specific variable, we wouldn't be able to access it without using :
if(M:movementceased)

mob/Click()
src.HP--
usr.movementceased = 1
//This would return a compile-time error because usr is defined as /mob.
//You would then have to use :, but then what happens if for some reason your
//character wasn't a /mob/Player (which would end up being a bug far later in
//the game development)? You'd get a run-time error.

Let's fix those:
var/mob/Player/movementceased = 0

proc/Proc1()
var/mob/Player/M = new
if(M.movementceased) //Now, if the mob in question didn't have that variable,
//it would come up as a compile-time error. Compile time errors are
//normally much easier to fix than run-time errors.

proc/Proc1(mob/Player/M)
if(M.movementceased)

mob/Click()
var/mob/Player/M = usr
src.HP--
M.movementceased = 1

The lesson here is that easiest or quickest isn't always best.



IV. Making smart decisions (Well, decision structures, anyways...)

Decision structures are one of the core components in your game, and they are often programmed horribly. Multiple if()s for the same thing, lack of switch() statements, and lack of else statements really hurt projects.

First off, I will show you one of the important aspects of an else statement -- an aspect you might not have realized before.

Take the following code for an example:
proc/Proc1(mob/Player/M)
if(M.Transformation)
M.Transformation = 0
if(!M.Transformation)
M.Transformation = 1

This isn't the best example, but it shows how it would cause an unsightly bug in your game. The function is supposed to make M.Transformation equal 0 if the mob's Transformation var equals 1, but because the statement below used a second if() instead of an else, it got processed immediately afterward, and just reset the var back to its original value.
proc/Proc1(mob/Player/M)
if(M.Transformation)
M.Transformation = 0
else
M.Transformation = 1

This would cause the bottom code to be processed only when the if() statement above is false.

Here is a more likely situation, much like the previous example:
proc/Proc1(mob/Player/M)
if(M.Transformation == 0)
M.Transformation = 1
if(M.Transformation == 1)
M.Transformation = 2
if(M.Transformation == 2)
M.Transformation = 3
if(M.Transformation == 3)
M.Transformation = 4
if(M.Transformation == 4)
M.Transformation = 0

The newbie would look at this and think that the proc will look at the variable and set it accordingly, but because of the lack of else statements, it would just reset the variable.

In this case, since there are many conditionals, we can keep them all on the same line by using the else if() statement, which is a handy combination:
proc/Proc1(mob/Player/M)
if(M.Transformation == 0)
M.Transformation = 1
else if(M.Transformation == 1)
M.Transformation = 2
else if(M.Transformation == 2)
M.Transformation = 3
else if(M.Transformation == 3)
M.Transformation = 4
else if(M.Transformation == 4)
M.Transformation = 0

Now I will let you in on a little secret: You don't need to write out M.Transformation for all of those conditional if() statements. Use a switch() statement instead. You put an argument into the switch() statement and it makes an implicit else if() chain for you. You also don't need to include the else if()s, only if()s -- because it's implied that all the if()s in a switch statement are else if()s. Here, let me show you:
proc/Proc1(mob/Player/M)
switch(M.Transformation)
if(0)
M.Transformation = 1
if(1)
M.Transformation = 2
if(2)
M.Transformation = 3
if(3)
M.Transformation = 4
if(4) //This could also be an else statement here at the end (not
//else if(), else -- which would act as a "catchall" for all values not handled above)
M.Transformation = 0

That would work without resetting the variable every time.

There is a disadvantage to the switch() statement, however. You cannot put full-fledged expressions in an if() statement used for switching. However, you can use multiple unique values separated by commas, or ranges of integers linked by "to" (for example, "if(1 to 10)").


Nothing kills time like hammering out 100 if() statements for a single proc.

Take a look at the code below and try to figure out what is wrong:
proc/Proc1(mob/Player/M)
//WRONG example
if(M.Timer == 60)
M.Cards += 5
else if(M.WonBattle == 1)
M.Cards += 5
else if(M.Handicapped == 1)
M.Cards += 5

What's wrong is that all of these conditionals do the exact same thing, so the author ended up continuing a conditional chain when he could have put it all on one line...

The magic of || and &&!

|| (Two of that straight line on the key above your enter/return key, usually called "vertical bar") means "or", and && means "and". These enable you to put multiple conditionals in a single if() statement:
proc/Proc1(mob/Player/M)
if(M.Timer == 60||M.WonBattle == 1||M.Handicapped == 1)
M.Cards += 5

If the player needs ALL of these statements to be true, then just replace the ||s with &&s:
proc/Proc1(mob/Player/M)
if(M.Timer == 60&&M.WonBattle == 1&&M.Handicapped == 1)
M.Cards += 5

Now, what if the code above was in use, but the player also had a few variables that could add the +5 bonus regardless of other factors. Since you would either have to have all three of the above conditionals OR one of the other variables, would you make another if() statement? You could, but once again, it's best to avoid multiple if()s that do the same thing.

You would need to include both || and && operators in the if() statement. Let me show you the wrong way to do it, first, however:
proc/Proc1(mob/Player/M)
//WRONG example
if(M.Timer == 60&&M.WonBattle == 1&&M.Handicapped == 1||M.AutoWin == 1||M.Aut1 == 1&&M.Aut2 == 1)
M.Cards += 5

&&s and ||s in the same set of conditionals makes the game go wacky because the computer uses an "order of precedence" to determine which operators it will handle first, and often this is not the order you intend. It's like telling someone you want "macaroni and cheese or tacos" for dinner and ending up with macaroni and tacos. To avoid this, you will need to group the subexpressions using parentheses:
proc/Proc1(mob/Player/M)
if((M.Timer == 60&&M.WonBattle == 1&&M.Handicapped == 1)||M.AutoWin == 1||(M.Aut1 == 1&&M.Aut2 == 1))
M.Cards += 5

That way, the first level of evaluation looks at the 3 && operators within the parentheses, instead of 2 ||s and 3 &&s. The comparisons will return a precise "true" or "false" value for each set of parentheses.



V. Procs, arguments, spawn()s, and returns


Another extremely important part of DM is working with procs. As I said in the definition section, algorithms are lists of instructions for tackling a problem, and procs are algorithms written in language that the compiler can understand.

The typical newbie knows how to make the basic proc, knows that src is the object the proc belongs to, and something like mob/M in the arguments list located in the parentheses will represent some monster or something. Newbies will often abuse usr by putting it in procs. Like this:
mob
proc/Attack(mob/M)
M.HP -= 5
M.DeathCheck()

proc/DeathCheck()
if(src.HP <= 0)
usr.Kills++
src.icon_state = "dead"
src.HP = 0

First of all, there is no guarantee that usr will have a useful value here. usr is only guaranteed reliable in the first proc or verb the player triggers -- this may sound like a bug in BYOND, but it's actually a deliberate design choice. In the example above, what if the mob doing the killing is an NPC? In that case, "usr.kills" would make no sense because usr only has a meaningful value when a verb or proc is triggered by a player's client.

Also, it is important to clearly define what is what. If someone else were to look at your code, they'd have to browse through your code to figure out what was usr and what was src. Wouldn't it be better if it were a bit more defined? Let's fix up DeathCheck() so it is better understandable:
mob
proc/Attack(mob/M)
M.HP -= 5
DeathCheck(M,src)

proc/DeathCheck(mob/Victim, mob/Killer) //Notice how it is now a global proc. We aren't using src anymore, so I made it
//global and defined the victim as mob/Victim. This is merely my style, however. It can be organized better
//if you make DeathCheck() a /mob proc.
if(Victim.HP <= 0)
Killer.Kills++
Victim.icon_state = "dead"
Victim.HP = 0

See? Now isn't that better?

Something that some people may be confused about is why there are multiple arguments... You were so used to having just mob/M as the only argument in these kind of procs, you didn't realize that it is completely customizable! Admit it!

That's right, when you define your own procs, like Attack(), MovePuzzlePiece(), or ShootFireball(), you can define those arguments however you like. Your proc can accept twenty arguments if you like (or even more).

Example:
proc/DeathCheck(mob/Victim,mob/Killer,obj/Item/Weapon/WeaponUsed,DeathMessage,ExplodingHit)

You could then call that like this:
mob
proc/Attack(mob/M)
M.HP -= 5
DeathCheck(M,src,src.Weapon, "[src] threw a bar of soap at [M]! ",1)

**Note that you can also set the defaults of arguments when you define the proc:
proc/DeathCheck(mob/Victim,mob/Killer,obj/Item/Weapon/WeaponUsed,DeathMessage = "An attack was made.",ExplodingHit = 0)

The objects are already null so we don't need to define them. However, we wouldn't want our text or number to be null, so we defined those. You could also set ExplodingHit to a default value of 5 -- you probably wouldn't want to if you're using it as a Boolean (true-or-false) variable, but you could.


spawn() procs:

Many newbies don't know what spawn() does or don't know about it at all. spawn() is used for starting new procs without holding up the current proc, or for delaying the execution of blocks of code.

Let's take the example of a DeathCheck() containing a sleep statement:
mob
proc/Attack(mob/M)
M.HP -= 5
DeathCheck(M,src)
src.Attacking = 0 //Let's say this var needs to be reset right away for some reason!

proc/DeathCheck(mob/Victim,mob/Killer)
if(Victim.HP <= 0)
Killer.Kills++
Victim.icon_state = "dead"
Victim.HP = 0

if(istype(Victim,/mob/Player))
sleep(10) //Uh oh, this ten-tick (one second) delay is going to hold up not only the
//DeathCheck() proc, but the previous Attack() proc as well.
Victim.icon_state = "alive"
Victim.loc = locate(1,1,1)

In the example above, because of the sleep statement in the DeathCheck() proc, if they were to kill a player, it would sleep(10) before setting src.Attacking to 0.

Here is how we'd fix that with spawn(). It creates a "spinoff" that works on its own without holding up the current proc:
mob
proc/Attack(mob/M)
M.HP -= 5
spawn()
DeathCheck(M,src) //You can also just do spawn() DeathCheck(M,src) all on one line
// as I do -- of course this only works if your spawn() only has to execute one line!
src.Attacking = 0

return statements:

return is an extremely useful statement that stops a proc and takes information from that proc back into the proc that called it. returns can be used to significantly shorten code.

As an example of it in action, many of DM's built-in procs return values. Enter() returns a 1 or 0 to determine if the object is able to enter the target; findtext() returns the starting position of the search text in a text string; locate() returns the turf at the coordinates specified, and so forth.

You use return by putting the information that is going to be returned after a space after the return statement, like this: return var. It would then return the information you put there back to the parent proc.

Example:

//Say you needed the tangent of a triangle... You could make a tangent() proc to find it and return the exact tangent without putting the math in the main proc:
proc/Calculate()
var/a = 5
var/b = 7.8
world << "The answer is [tangent(a,b)]."

//This will return the mathematical tangent of what you put into the proc.
proc/tangent(opposite,adjacent)
return (opposite/adjacent)

Another example: say you need to check if a turf is completely clear of anything dense. Simply checking the turf's density will not do, because there may be a dense obj or mob in it.
proc/MoveMob(mob/M,turf/T)
if(!T.isdense()) //If /turf/isdense() returns 1, the if statement will
//be true because everything but 0 means true, but if it returns 0, then the
//if() statement wont process.
M.loc = T

turf/proc/isdense()
if(src.density) return 1
for(var/atom/A in src.contents) //For any atoms on that turf...
if(A.density) return 1

If the for() loop passes through the whole contents list without returning a 1, then the proc will automatically return a null value, which will be treated as false.
what do you do
i thought this was a newbie guide. lol. but it helped me a little ^ ^ thanks.
omg i dunno how to do this someone wunna help me :D?
This tutorial only goes up to V (5) :(
Where's the rest?
go kunark!
buashahahahahahha
WTH i cant understand this guide man!
cant understand got headack now
lol(@ the other comments) thanks for posting this guide man. You should be my mentor so you can help me make a cool game :-p
Areas descend from atom, not datum.
opoýýkk
i think coding on a ti 83+ calculator is easier than this...
how do u actually do this
Zeratul ultra wrote:
i think coding on a ti 83+ calculator is easier than this...

Oh yeah, TI-BASIC very nice. Long time to write out, but simple enough.
i got 2 problems i got a source from a friend idk how to make me owner on it or how to make it so on my hub i made it says its live :(
Im using the google chrome browser and in some text i get this â€ÂÅ“An
What?
These articles are still being pointed to in high-up developer resources (like front page). So I'm suggesting all the â€ÂœAn stuff gets fixed for the text encoding. That's all!