| Revision History | ||
|---|---|---|
| Revision 7 | 26 December 2001 | rjh |
| Added Nadrew's code for delaying mob movement. | ||
| Revision 6 | 26 December 2001 | rjh |
| Added Spuzzum's firewall answer and cleaned up some obsolete links and text. | ||
| Revision 5 | 10 August 2001 | rjh |
| Added Air Mapster's teleporting answer. | ||
BYOND is a free object-oriented multi-player game development system, created by Dantom (two guys named Dan and Tom, naturally enough). This document is a central point for answering questions about BYOND and helping you start on creating your own game. A major goal of the FAQ is to encourage you to use the rich set of resources available to you, so frequently questions will be answered by pointing you to the appropriate place in the documentation or to some sample code that is available.
The FAQ was originated by Gabriel <gjsfaun@hotmail.com> and is maintained by designers creating games with BYOND. If you have any suggestions for improving the FAQ, email <ron@deadron.com>.
Here are some of the information sources you should definitely check out in addition to this FAQ:
The BYOND home page, http://www.byond.com
The BYOND forums, http://www.byond.com/forum/forum.cgi?action=forum_index
The BYOND download page, http://www.byond.com/download.html
The Dream Maker: Designer's Guide to Worlds BYOND, http://www.byond.com/products/dmguide/index.html
Table of Contents
Everything you need to develop and distribute BYOND games is free. The development environment, the server, the client, and the hub infrastructure for listing and publicizing your game are all free. You can create the games and people can download, play, and host the games for no cost.
If you wish to run a persistent world on a server, then you may need to pay a hosting service for their server facilities. You can host BYOND games from pretty much any Windows or Unix machine. In general Dantom encourages you to try for games that people can host on their own machine to play with their friends. It's rare for persistent games to get finished. However BYOND fully supports persistent worlds if you have somewhere to host the game.
There are some MUD providers out there who are rumored to let you host MUDs for free (though they seem to be disappearing), or you might be set up to host Internet-based games on your own server. If you wish to use a free service there are no barriers to doing so. Dantom will probably even help get the BYOND server running if you ask.
BYOND was originally designed to support 2D-graphic MUDs and text-based MUDs. Over time it has grown to become much more generalized and powerful, and now the majority of games created fall into other categories.
You can create just about any game you can imagine, with one primary limitation: BYOND uses a 2D tile-based graphic model, as described below. So First Person Shooters and 3D worlds are pretty much out of the picture, but just about anything else goes. BYOND supports client/server, peer to peer, single-player, and web-based games. The web-based games actually use BYOND executables just like cgi scripts.
Here is a sampling of a few games currently registered with the BYOND hub at http://www.byond.com/hub:
Sheep II, DragonSnot, and the Slurpy Demo are classic-style arcade games.
DBZ Spar and SidewalkSantas are action games.
Lexiconomy and Conflict and Sixes are text-based party games.
Backgammon and Chess and Drummond Cribbage are turn-based classics.
There are also several MUDs in development, and you can check out the beta versions by looking at the hub.
BYOND is one of the best chances a non-programmer has for getting a game created. But here is something you should be aware of from the beginning: You are not going to be able to do any substantive game in any system without learning some programming.
BYOND is an excellent tool for you to get started. The language is object-oriented, clean and uncluttered, yet is a full-featured programming language that lets you do anything you can think of. The less you know about programming, the more you should start your programming career by buying The Dream Maker: Designer's Guide to Worlds BYOND . It is an excellent book that will simultaneously teach you how to program and save you many hours of frustration. If it seems like this FAQ is pushing the book on you, you are catching on. Because when people don't read the book, the authors of this FAQ end up answering many of the same basic questions over and over again.
Make us like you: Read the book.
The hub is the main place players go to find BYOND games. It lists the registered BYOND games and provides download and connection links. It also lists the live games currently being played online and allows you to join. There is no requirement to register your game; the hub is there to help publicize your work if you want to use it. The hub page is http://www.byond.com/hub.
BYOND supports 2D tile-based maps. Each tile ("turf") is 32x32 pixels. mob and object icons are also 32x32. There is support for placing larger .bmp files on the map, and it's possible to create mobs and objects with larger icons once you know what you are doing. This is discussed later in the FAQ.
While the 32x32 tile situation can be somewhat limiting (at least until you learn some tricks), BYOND does support a reasonably rich set of graphic functionality. Objects and mobs can have movement animation, overlays (extra icons on top, such as armor), underlays (extra icons underneath), and different icon states. You can also use icon arithmetic to adjust an icon's colors or merge icons. The best place to start learning about BYOND graphic support is the icon tutorial, at www.byond.com/hub/hub.cgi?hub=Tom.IconPrimer.
The language is called DM, and was created specifically for BYOND. It is object-oriented and extremely flexible, using a C/C++ inspired syntax, but without any of the confusing aspects of C++ or Java. People new to programming will find this about the friendliest language around, while experienced programmers will find they can do pretty much anything with it.
To provide a slightly more technical summary, DM is a dynamic-binding, introspective, fully-polymorphic, single-inheritance language. Objects know what they are, can be queried at run-time as to their attributes, and lists can contain arbitrary sets of objects. Thus there is no need for templates. DM has no concept of protected or private functions, and there is no custom operator overloading (though BYOND has some built-in operator overloading). You can extend or override almost any object or function in the system.
If you have any object-oriented programming experience, BYOND is quite easy to learn and you can be up and running in a couple of hours.
If you have procedural programming experience (in other words, not object-oriented), you can get up and running in the manner you are used to pretty quickly, by ignoring the object-oriented nature of BYOND. Eventually you will want to learn and use the object-oriented features, which takes some time. The usual process is to write a small game in whatever manner you are used to, then with your second game move to the object-oriented approach. It generally takes a few weeks to get the hang of object-oriented concepts.
If you have no programming experience at all, then you've come to the right system, but you need to be prepared to do some work. The wrong (and unfortunately common!) approach is to have an epic game in mind and jump in and try to build it straight away. That approach inevitably leads to frustration. It takes time to learn to program. It takes time to learn a system. The best thing to do is set a small goal for yourself (something like "I want two characters to fight until one is dead" or "I want an NPC that talks back to me"), then follow ALL the steps listed in the "How Do I Get Started" section below, then complete your goal. Then make another, slightly harder goal for yourself and solve that. Etc. Eventually you will have a game. This can take as little as a week if you are ready to put in some time, but be nice to yourself and give yourself a month to get your first little game in place. After that, you'll find yourself able to complete small games with just a few days of work.
Of course there many factors to this question...in general your first game takes a while to get right, especially if you are new to programming (though new people have managed it in as little as a week!). Usually it's not until your second or third little game that you really know what you are doing. Once you are familiar with the system, a very simple text-based game only takes a day or so to put together. Simple graphic games take a few days to a week. Complex games are completely dependent on what you are doing, of course.
Yes! Though, of course, there is no requirement to do so. BYOND provides a full-featured financial system using "BYONDimes" to allow you to support subscriptions and collect payment from players if you wish and to sell products through the BYOND website. The details are explained at http://www.byond.com/products/BYONDimes.html.
Dantom encourages you to provide some free form of your game and some version that players can choose to pay a small amount for. For example, you can download the entire DragonSnot game, but can only play half of it until you've paid for a lifetime subscription, which costs 20 BYONDimes ($2.00 US).
If you don't want to engage in actual programming, then you won't enjoy BYOND.
If your game depends on first-person 3D graphics or a very high framerate (more than 10 fps), then BYOND is probably not for you. But give it some thought. Many games seem like they require more originally, but actually they don't.
The development environment is included in the standard BYOND package at http://www.byond.com/download.html. The development application is DreamMaker.exe, which you'll find in the BYOND/bin directory after you install the package.
Start with Zilal's Beginner's Tutorial, at http://www.byond.com/hub/hub.cgi?hub=Zilal.ZBT, and try out the Your First World demo, at http://www.byond.com/hub/hub.cgi?hub=Dantom.YourFirstWorld.
Yup! It's called A Step BYOND, and it's fully commented to explain how it works. You may do well to use this game as your starting point, and change the code to turn it into your own game. You can download it at http://www.byond.com/hub/hub.cgi?hub=Deadron.StepBYOND.
In addition to the things listed above, there is:
The BYOND tutorials, http://www.byond.com/hub/DM/Tutorials
The DreamMaker Online Help, which you can read while using DreamMaker by hitting F1. Experienced BYOND coders use the online help constantly to make their lives happier.
The DM Language guide, http://www.byond.com/code/guide/index.html.
The demos, http://www.byond.com/hub/DM/Demos.
The libraries, http://www.byond.com/hub/DM/Library.
The official (and very good) printed book The Dream Maker: Designer's Guide to Worlds BYOND, http://www.byond.com/products/dmguide/index.html.
Fortunately, there is a thriving BYOND community ready to help you out on the BYOND forums, at http://www.byond.com/forum/forum.cgi?action=forum_index. When you have a question to ask, you will greatly enhance the chance of getting it answered if you do take the following steps:
Use F1 in DreamMaker and search the online help. Very often the answer will be there.
Look for documentation/tutorials/sample code that BYOND coders have taken the time to provide for you and see if they answer your questions. Many of them are pointed to in this FAQ or from the DM InfoCenter.
Read through this FAQ carefully and see if your question might be answered somewhere.
Do a search in the BYOND forums (especially the Newbie forum) to see if the question has already been answered.
If none of these does the trick, then feel free to ask! Please provide a full description of any errors you are getting, and as much of a code sample as you can (even better if you zip up the game source and point to it on your website). Whatever you do, don't post a message that says something like "My combat system doesn't work, please post the complete code for a combat system and I really need it today." Believe it or not we get probably a dozen requests like that a week, and it's just not possible to answer all of them.
This might seem like a lot to ask of you before you ask a question...but think of it this way: if you aren't willing to put an extra 10 or 20 minutes into trying to solve your problem, why should other BYOND coders put as much or more time into answering it for you?
That said, BYOND coders love talking to other BYOND coders, so please come to the forums and introduce yourself before you ever run into a problem!
There is a Host button on the bottom right of Dream Seeker. This allows you to host your current game and specify the level of privacy for the game. People may then subsequently join you through the pager or through the Games Live! page on the BYOND website.
Also note that the Live! page is accessible through Dream Seeker itself by clicking on the Hub button. At the top of the page that comes up (it may take a while, since it uses Internet Explorer ;-) you will see the link to the Live! page. Click on it, and your game will probably be on the list there.
- (Spuzzum)
Though the exact process tends to vary with the sofware, there are several steps that can be done on your end to ensure that your firewall and DD/DS are smiling politely at each other.
A reference is available at the following URL: http://www.byond.com/docs/howto/byond_network.html.
- (Spuzzum)
After you've spent a while programming, you may forget the details of how your older procs work. Sooner or later, though, you'll probably need to go back to them and make changes. When you do, it's a relief to be able to read them without investing hours of study. By writing small procs with clear names that describe exactly what they do, you'll find it much easier to follow the flow of your code. (And if you work on a large project with multiple people contributing code, this technique is even more important.)
Also, when you have small, single-purpose procs, it's easy to reuse them throughout your code. This means that when you decide to add complexity, you'll only have to make a change in one place. It also greatly reduces the risk of overlooking one or two edits and ending up with a program that acts inconsistently.
- (Guy Tellefsen)
Two kinds of programmers typically create their first Object-Oriented game using one big function: those completely new to programming and those with a strong procedural coding background in languages like C. It's a natural way to start, and it makes total sense to do your first game that way so you can get up and running. But once you know the lay of the land, you'll find that the "one big function" approach keeps you from enjoying all the benefits of an object-oriented language.
For example, you can have a long function that looks at each NPC mob in your game and says, effectively, "If this is a goblin, do this; if this is a dragon, do this; if this is a paladin, do this". For a while that will work fine, but it gets big and complicated fast, and it makes it hard to add new mobs because you have to fit them into that mass of code somewhere. It's much better for mobs to have, say, a PerformTurn() function. You can define default mob.PerformTurn() behavior that most mobs follow, and if Goblins do something special, you create a goblin.PerformTurn() function that does its special thing and ignores the default behavior. Then if you decide to change how the default behavior works, you change it in one place, and all the appropriate mobs immediately change their behavior and the others ignore the change.
This is only one of the many benefits you get from breaking your code into object-oriented chunks. You'll learn many more as you go along. The main point is: start programming in whatever way makes sense to you, but once you've figured out the basics of putting a game together, go back and start using the object-oriented techniques. You'll make much better, more maintainable, more extensible games. Sample source code like A Step BYOND, at http://www.byond.com/hub/hub.cgi?hub=Deadron.StepBYOND, provide good examples of how to structure an object-oriented game.
- (Deadron)
These are objects that can move around a BYOND map. They're very similar, except mobs can have a client attached, and objs can't. Attaching a client to an object allows a person to play as the object. So if some players will be able to play as teacups, you should define teacups as mobs; otherwise, you can save a little memory by declaring teacups as objs. (In theory, you could just leave out objs entirely, and make everything a mob. But your program will use up fewer resources if you only use mobs where they're needed.)
Don't be confused by the difference between "obj" and "object". mob, obj, area, and turf are all objects. An object is anything that can have its own vars and procs.
The BYOND guide discussion, http://www.byond.com/code/guide/info.html#1.2
The BYOND reference mob entry, http://www.byond.com/code/ref/info.html#/mob
The BYOND reference obj entry, http://www.byond.com/code/ref/info.html#/obj
- (Guy Tellefsen)
Either it's a built-in var, or you've already declared it as a var. For example, this is wrong:
mob
var/health
player
var/health //...BAD example because we already declared "health" above.
var/turf/loc //...BAD example because "loc" is a built-in var for all mobs.
- (Guy Tellefsen)
While you can't declare a variable twice, you can override the default variable definition anywhere you'd like. So if your mob code defines the health variable as shown above, and you want player mobs to have a specific number of health points when they are created, you would do so like this:
mob
var/health
player
health = 100 // This sets player.health to 100 when the player is created.
Notice how the player health variable doesn't start with var, as in var/health. This is because you use the var term only in the class where the variable is initially defined. The subclasses of that class refer directly to the variable without using var/.
- (Deadron)
DM is very finicky about making sure everything is in its proper place. Here's a quick sample of clean indentation:
mob
var
health
proc
AnnounceHealth()
if(health <= 0)
world << "[src]: Unfortunately, I'm dead."
else
world << "[src]: My health is [health]!"
Notice how a line that is owned by the line above is indented. You can use whatever spacing you'd like to indent, whether it's 3 spaces for each indent or a tab (a tab is the recommended form). Whatever way you choose to indent within a function, BYOND expects you to keeping using that same spacing for the indentation, which can lead to mysterious indentation bugs. Sometimes code will look perfectly fine, but indentation errors will still occur. If you're using tabs, this often means there's a space hidden somewhere; for example, a line indented with two tabs might look the same as a line indented with a space and two tabs. If you're using spaces, make sure you count them consistently; if you use 3 spaces for the first indent level, the second indent level should be 6 spaces, the third should be 9, and so on.
- (Guy Tellefsen)
When you get an indentation error (or dozens of them!) that you don't understand, here is a tip for fixing it: Go to the first line that reports an indentation problem, delete all the white space in front of the line, and reindent it (if you are using tabs, just tab it over until it's in the right spot).
Now recompile and see if that fixes things. Often you only need to fix one indentation problem for the other errors to go away, though sometimes the error has slipped into several lines. Also, this happens a lot with code you paste into your game from somewhere else -- you may need to reindent all the pasted code.
And one more tip: The BYOND text editor lets you tab-indent and unindent multiple lines at a time. If you select a number of lines and hit tab, they will all be indented by one more tab from where they start; if you shift-tab, they will be unindented by one tab. This is extremely useful!
- (Deadron)
Either the object does not have a var with that name... or, more confusingly, the object has the var, but the compiler doesn't know it! Here's how that could happen.
mob/var/health
verb/attack(M as mob in view(usr, 5))
M.health //...BAD example because compiler doesn't know M is a mob... read on!
This example may seem downright crazy, because you just told the compiler M was a mob, right? Well, right, but also wrong. "as mob" is actually a command that will help Dream Seeker prevent the player from entering any target that isn't a mob. So Dream Seeker, and we, know that M will be a mob... but from the compiler's point of view, M could be anything, because we didn't declare its type. The solution is simple, though it does sound a bit redundant when you read it out loud:
verb/attack(mob/M as mob in view(usr, 5))
- (Guy Tellefsen)
A common place for the "bad var" error to come up is with the usr variable. In verbs, usr represents the player who clicked the verb. So you might have a mob/player class, and give players an isGM variable, like so:
mob/player
var
isGM
Then say you have a delete_game() verb that only GMs can call, and you implement it like so:
mob/player
verb
delete_game()
if (usr.isGM != 1)
usr << "You are not a GM so you can't do this!"
return
This looks good, but it will generate a "bad var" error. Why? Because usr is a BYOND variable which is declared as a plain /mob. Given that your isGM variable belongs to /mob/player, usr doesn't know anything about it. To solve this you could either use src.isGM or, in cases where that isn't appropriate, assign usr to a /mob/player variable, like so:
mob/player
verb
delete_game()
var/mob/player/the_player = usr
if (the_player.isGM != 1)
usr << "You are not a GM so you can't do this!"
return
- (Deadron)
You forgot to set world.mob. Look at the following example:
mob/player/Login()
..()
world << "Hello world!"
Try it out, by itself (i.e. its own individual project). What happens? Absolutely nothing! You'll get a map (if you have one) but you won't get any text. This is because in this case, you've created a /mob/player type, but world.mob is still at its default, /mob. So you need to explicitly set world.mob:
world/mob = /mob/player
mob/player/Login()
..()
world << "Hello world!"
Read the "Connection Process" tutorial, at http://www.byond.com/code/tutorial/connect/connect.html, for more info on what happens when you connect to your world.
(If you're wondering what's actually going on, it's because when a player logs in, instead of being connected to /mob/player like you'd expect, it gets connected to what world.mob is. Thus, /mob. Because /mob/player has the Login() proc, not /mob, you don't get any message. Once you set world.mob to /mob/player, it works!)
- (Spuzzum)
You've forgotten the ..() instruction. Try this out (with a map):
mob/Login()
world << "Hello world!"
When you log in, you get the delightful message 'Hello world!' But no map. Why? You've forgotten ..()!
mob/Login()
..() //the ..() instruction makes it do the default process
world << "Hello world!" //and display a lil' message
..() is a special proc that tells the compiler to put the normal code that this proc used to do in that position. Since Login() connects the player to a mob by default, if you don't call ..(), then you don't get a specific mob. Tough break. =)
This is also covered in the "Connection Process" tutorial, at http://www.byond.com/code/tutorial/connect/connect.html. RTFM! =).
- (Spuzzum)
src is the object that owns the current proc or verb. For example, if you have a proc obj/computer/proc/Shutdown(), then when Shutdown() is called, src will be the computer object. (If you are familiar with other programming languages, src is much the same as "this" or "self".)
The BYOND guide discussion, http://www.byond.com/code/guide/info.html#1.4.1
The BYOND reference entry, http://www.byond.com/code/ref/info.html#/proc/var/src
- (Guy Tellefsen)
usr is the object that used a verb. This will always be a player's mob, unless you're calling verbs from within your own code, which can get complicated. In general, if you ever want one of your NPC's to be able to do something that the player can do with a verb, it's best to keep the verb short, and simply call a proc that does the thing you need. For example:
mob/verb/say(T as text)
Say(usr, T)
proc/Say(mob/speaker, T)
view(speaker, 5) << "[speaker]: [T]" //...Heavy-duty NPC-safe example
This is better than putting all the login inside the say() verb itself, because the value of usr is only set when someone chooses a verb in Dream Seeker. So if an NPC tried to use the say() verb instead of the Say() proc,it would end up getting an unpredictable value of usr... it could be null, or it could even be the last player who used a verb!
Essentially, usr is an implicit parameter that is passed to every proc or verb. Each procedure inherits the value from its caller. While it can simplify your code in some situations, it can also lead to subtle problems if you are assuming that usr is automatically assigned to src when you call a verb programmatically. It is not.
The only time usr is assigned for you is when a player (in Dream Seeker) initiates the action: executing a verb, clicking something with the mouse, clicking a movement key, and so on.
The BYOND guide discussion, http://www.byond.com/code/guide/info.html#1.4.1
The BYOND reference entry, http://www.byond.com/code/ref/info.html#/proc/var/usr
- (Guy Tellefsen)
There are two procs for this purpose; input(), which allows for flexible choices, and alert(), which pops up a prompt with buttons.
The input() proc is used in the following format:
var/variable = input(mob, "Detailed explanation", "Title", "default_choice") as anything in list
This sends a prompt to mob, who then picks a choice from the selection given, and that choice is stored into the variable.
The alert() proc is used in the following format:
This sends a pop-up alert to mob, who then picks a button (in this case, "Button1" or "Button2", and that choice is stored into the variable.
var/variable = alert(mob, "Detailed explanation", "Title", "Button1", "Button2")
The BYOND input() reference entry, http://www.byond.com/docs/ref/info.html#/proc/input
The BYOND alert() reference entry, http://www.byond.com/docs/ref/info.html#/proc/alert
- (Spuzzum)
To avoid confusion, when visibility is set to 0 the player can still see themselves. If this were not the case, players would have a great deal of difficulty navigating properly.
If you really want the player to turn invisible, even to themselves, set visibility to 0, save their icon into a variable, and then set their icon to null. This won't remove overlays or underlays, however, unless you save those lists as well.
Here is an example snippet (which preserves the overlays):
mob
var
visible_icon
visible_overlays[0]
visible_underlays[0]
proc/TurnInvisible()
if(src.visibility) //makes sure you can't turn invisible twice
src.visible_icon = src.icon
src.visible_overlays = src.overlays
src.visible_underlays = src.underlays
src.visibility = 0
proc/TurnVisible()
if(!src.visibility)
src.icon = src.visible_icon
src.overlays = src.visible_overlays
src.underlays = src.visible_underlays
src.visibility = 1
- (Spuzzum)
Each verb has a setting called 'category'. The category setting contains a text string that will set the panel where the verb will appear. Verb settings are changed by using 'set [setting] = [value]'. The category setting is changed in the following format;
mob/verb/say(T as text)
set category = "Communication"
world << "[src]: [T]"
and in this example, the verb would appear in the Communication panel.
- (Spuzzum)
These are fairly standard verbs that players use, and no one has the correct answer as to which method should be used the most; the first puts the verbs in the obj to get or drop, the second gives the verbs to the mob that affect the obj... each has their benefits and drawbacks. The first method looks like:
obj/verb/get()
set src in oview(1)
if(src.Move(usr))
oview() << "[usr] gets [src]."
usr << "You get [src]."
else
usr << "You can't get [src]."
obj/verb/drop()
set src in usr
if(src.Move(usr.loc))
oview() << "[usr] drops [src]."
usr << "You drop [src]."
else
usr << "You can't drop [src]."
The second method looks like:
mob/verb/get(obj/O in oview(1))
if(O.Move(src))
oview() << "[src] gets [O]."
src << "You get [O]."
else
src << "You can't get [O]."
mob/verb/drop(obj/O in src)
if(O.Move(src.loc))
oview() << "[src] drops [O]."
src << "You drop [O]."
else
src << "You can't drop [O]."
Each method has its benefits and drawbacks. The first method makes it so that the user can only click the verb when he or she is next to an object... however, this makes it difficult for mobs controlled by the AI to pick items up, because usr is usually a mob with a player. usr must be set before the mob can pick it up, which is an unnecessary hassle. The second method removes this restriction, but unfortunately the verb is always present (i.e. the player can always click it or type it in), even if there is nothing to get or drop.
It's up to you to decide which one you want to take; for most intents and purposes, I'd recommend the first one, though, because most of the time your AI shouldn't be picking things up anyway.
- (Spuzzum)
BYOND supports bmp files for simple icons and its own dmi format for more complex icons that use animation and have different states. Though you can use a bmp file directly, the usual process is to create bmp files in a graphics editor, then copy them into dmi files in BYOND. Currently 256 color icons are supported; more colors are likely to be supported in the future.
At this time, gif and jpeg and other image types are not supported, though you can use a graphics editor to copy such an image, then paste it into a dmi file in BYOND.
You can't just drop an icon onto your map. You place turfs, objects, and mobs on the map, and assign icons to them. Say you want to add a barrel image to your ocean map. To do so, create a barrel object and assign your barrel icon to it:
obj/barrel
icon = 'barrel.bmp'
Notice the single-quotes used there...that indicates a resource that needs to be compiled into the game dmb file. That's pretty much always the case for icons, so you should always use the single-quotes when assigning them. After you compile your code, the map editor will let you place barrels on the map. Or you can create them in your code.
Because that barrel used a bmp file, it will be a boring barrel. bmps don't support animation or icon states or anything. The remaining questions in this section talk about how to jazz up your icons. You can create or edit icons using DreamMaker's Icon Editor. To see the Icon Editor documentation, use DreamMaker's Help->General menu item.
Yes, for .dmi icons you can have up to eight directions for any icon or animation. All objects that can be displayed on the map have a dir variable that indicates the direction they are facing; BYOND will check the object's icon to see if it has an image for facing in that direction.
To set this, though, you have to treat the icon as an animation, whether it has multiple frames or not. If it's just a single image, then you are simply creating a one-frame animation. You can turn a single icon into an animation by right-clicking on the icon state and choosing Edit as Movie. In the editor use the #Dirs popup at the bottom of the editor to specify how many directions the icon or animation has. You then need to provide an icon or animation for each direction. A common approach is to copy the same icon into each direction slot, then double-click on a direction slot and use the arrows at the bottom of the icon editor to rotate the icon appropriately. For more information on how to edit icons, use DreamMaker's Help->General menu item.
An icon can have different images or animations to display for different circumstances, such as a living and a dead version of a Goblin, and each of these is called an icon state. A dmi file is made up of one or more icon states. When you open a dmi file in DreamMaker, each image you see represents one icon state for that dmi file; the icon state can be a single image or an animation. You can double-click the icon state to edit it, or right-click and use the content menu. In the simplest case, the dmi file has only one icon state, and that is used whenever that dmi file is used.
For more complex situations, objects that can be displayed in the map have an icon_state variable. By default this is null, but you can set it to any value you want. If an object's icon_state variable is set, then BYOND looks in the dmi file for an icon state of the same name.
So say you have a barrel which is normally a fine upstanding barrel, but when dumped into the ocean it bobs up and down. To implement this you need two icon states: one for just standing there, and one for floating in the water. So you would add a single image for standing there, and leave it untitled so it is the default barrel image. Then you would add a movie icon state showing the barrel on its side bobbing up and down. This floating icon state needs a name...you can give it one by double-clicking directly underneath the icon state and setting the name to something like "floating". Then your code would change the barrel's icon state like so:
obj/barrel
icon = 'barrel.bmp'
proc
Float()
icon_state = "floating"
For an example of using icon states, see the Icon Management tutorial, at http://www.byond.com/code/tutorial/icon/icon.html.
It can be a bit confusing at first to understand the difference between a movement state and an icon state. A movement state is an icon state that is only displayed while a mob or object is moving between turfs. Whenever an object moves, BYOND remembers its icon state and checks the dmi file for a movement state with the same icon state name. If it finds one, it shows that image or animation while the object is moving. Let's say your mobs have a special icon_state called "on_fire". If a mob with the "on_fire" icon state moves, here are the steps BYOND goes through:
Look in the mob's dmi file to see if there is a movement state called "on_fire".
If so, display the "on_fire" movement state image or animation for the direction the mob is moving in, until the mob reaches the next turf.
When the mob has reached the next turf, display the normal "on fire" icon state again.
So a single mob or object might have several icon states and each of those icon states can have its own movement state, by having each movement state named after the appropriate icon state. To specify that an icon state is a movement state, double-click directly below the icon state, and in the editing window that comes up provide the icon state name and check the "Movement state" checkbox. Once you do this, an "M" appears next to the movement state and it will only be shown while the mob is moving and has the appropriate icon state set.
Using the terms "icon state" and "movement state" so many times close together is confusing. It might take some exploration to understand. For an example of using movement states, see the Icon Management tutorial, at http://www.byond.com/code/tutorial/icon/icon.html.
At this time BYOND does not support animated gifs, so you need to use BYOND's dmi format. This works well because dmi files are a quite rich format to use. To turn an icon into an animation, create a .dmi file in DreamMaker, then click on the movie camera to add an animation to the file. If you want to turn an existing single image into an animation, right click on the image and choose Edit as Movie. Each icon state in a dmi can be an animation, and each movement state of an icon state can be an animation. Sometimes it's a bit confusing to understand the difference between a "pixmap" (single image) and a movie in the icon editor. The reality is that a pixmap is simply treated as a one-frame movie.
Overlays are images drawn on top of an object's icon, and underlays are images drawn beneath the icon. Overlays are often used to provide clothing and armor for mobs, as well as health bars and the like. Overlays and underlays can have animations and movement states like any icon. You add them to an object like so:
mob
proc
add_armor()
overlays += 'armor_over.dmi'
underlays += 'armor_under.dmi' // underlays are not nearly as common as overlays.
For a practical example of using overlays, see the Icon Management tutorial, at http://www.byond.com/code/tutorial/icon/icon.html.
The layer variable, a recent addition, makes it so you can force an object to appear above another object. There are six default layers:
When in the same turf on the map, objects at the same layer will display based on which was there first (being the lowest) and which was there last (being the highest). With the exception of the FLOAT_LAYER, any object at a lower layer than another object is guaranteed to appear below that object.
The FLOAT_LAYER is a special layer that applies to overlays, underlays, and images (see their respective documentation). An object at the FLOAT_LAYER will appear on the same layer as the object to which it is attached (with the exception of underlays, which will appear below the object to which they are attached). For example, a mob's overlay will appear above that mob, but below subsequent mobs in the space.
- (Spuzzum)
There are two ways of doing this; BYOND, though it isn't fully capable (just yet) of using large bitmap images for mobs and objs, you can set a turf's icon to 'blah.bmp', and then when you draw the turf in the map editor, it will draw the full bitmap image, with where you clicked being the lower left corner of the picture. You can also set an area's icon to 'blah.bmp' to have it tile it nicely; areas only show up if the turf above them doesn't have an icon, though.
The second method involves using a third-party utility to break up a picture into 32x32 icons, browsing through those icons using a paint program, then copying them one-by-one into a .dmi file in Dream Maker. Obviously, this is a long and drawn-out process, but the effects are sometimes worth it. A good program for this is Split Image, available at http://www.thecastle.com/software.html.
- (Spuzzum)
Yes. There are a couple of ways to do this, depending on what you are doing. If you assign a large bmp file to an object and place that object on the map in the map editor, BYOND will cut the bmp up into 32x32 chunks and create multiple instances of the object, one to hold each chunk of the image. This is useful for background decorations and big buttons and such. Things that don't move around. Right now it only works for large bmp files, not other formats.
If you wish to have a moving object or mob with an image larger than 32x32, BYOND doesn't support that directly but you can do so with a bit of work. Paul Mikell <paul@pmikell.freeserve.co.uk> has provided a tutorial called "How to implement multi-tile mobs", at http://www.pmikell.freeserve.co.uk/byond/examples/multimob.html.
Yes. Use the image(), which is documented in the F1 online help and in the reference at http://www.byond.com/code/ref/info.html#/proc/image.
Simply put, you can't. Heh, no, just kidding. It's actually encouraged that you don't do this, because it promotes continuity between games and makes it easier on the eyes. However, you can also say to other people that "I read the FAQ and know what I'm doing! So there!", which is also always A Good Thing.
It is done by using a .dms file using a subset of HTML called CSS (Cascading Style Sheets); there are three relevant sections pertaining to how to use CSS:
The simplest thing you need to know is the BODY section;
which will display white text on a black background. You don't actually need to know HTML colour codes either, though (the hoopy #XXXXXX thingies). The following will work too:
Simply create a new .dms file ( File->New in Dream Maker, choose .dms file from the drop-down list, and type a name), then put in that code, replaced by your own custom colour scheme.
- (Spuzzum)
Savefiles are a very convenient, compact, and fast way to store game data. Almost all games need to make regular use of savefiles for storing player info, character data, world information, etc. You can save objects, mobs, or arbitrary data directly to a savefile. A savefile is not a true database, but a way to have a hierarchy of data inside a file.
BYOND also provides full access for reading and writing your own files, so you can bypass savefiles if you wish. Most likely, though, you'll find them to be what you want for storing data.
Savefiles are really very simple to use, but people tend to find them confusing at first. This is one where reading The Dream Maker helps a lot.
Whenever you send output, it will be sent to anything in the list given. So
world << "Hello, World!"
will send output to everything in world.contents, which basically means every single mob, obj, turf, and area. Of course, if it is possible to send output to a certain thing, then it won't send output; so if a mob doesn't have a client, then it would be useless to send output to them, and BYOND simply ignores that request.
When you want to send output to a specific person, it is not much different. You create a variable that contains the mob that you want to send output to, and then you use << to send it:
mob/verb/attack(mob/M in view())
usr << "You attack [M]!"
M << "[usr] attacks you!"
This can easily be easily adapted to let players send secret messages to each other:
mob/verb/whisper(mob/M in view(),T as text)
usr << "You whisper to [M]: [T]"
M << "[M] whispers to you: [T]"
- (Spuzzum)
There are tons of ways to do everything! This is no exception. There are three different ways that this is commonly done. The locate() proc is your best friend, and it is used in all three methods. The second method can also be used in traditional room-based MUDs as well.
The first method involves locating the player to a specific coordinate whenever they log in. That is,
mob/Login()
..()
usr.loc = locate(5,5,1) //locate the player to x:5,y:5,z:1 on your map
Fortunately, this is a guaranteed way to log the player in, because they will never appear off the map (read on).
The second method finds an unoccupied space with a specific area (that you have drawn onto your map, except in the case of a room-based MUD), and then moves the player into it.
mob/Login()
..()
usr.Move(locate(/area/myarea))
Unfortunately, if all of the possible spaces are occupied, the player will appear in blackness called null loc (except in the case of a room-based MUD, where any number of players can occupy a 'room'). However, this is the only easy method (there are more advanced methods that you can do in time) which doesn't allow you to appear in the same spot as other players if another player logs in before you.
The third method is the following:
mob/Login()
..()
usr.loc = locate(/turf/myturf)
This has no chance of locating to null loc, which makes this a preferred choice. Unfortunately, all players logging in will appear in the same spot, occupying the same space, which is somewhat strange.
For the most part, you should use the second, because it gives you the greatest adaptibility. Just draw a large enough area on the map, and all of your players will log in happily.
- (Spuzzum)
Easiest way to do this is define a special type of area or turf called teleport. Which you use (area or turf) depends largely on what you want to do with it - but either will work. Area is more flexible because you can put lots of different types of turfs on top of it. So first, define it:
area/teleport
Cool. Now, what do we want to have happen? When you get to the edge of the map, you should be teleported to a new map level (Z level) somewhere. How do we know when the player reaches the edge? We make the map spaces along the edge all have area of /area/teleport. Then we use the Entered() proc. That proc gets called any time a player enters the teleport area.
So, now we have:
area/teleport
Entered(mob/M)
What do we want to put in there? Probably the code to relocate the player to the new map level. But how do we know where that is? We could hardcode the x,y,z values in a locate() call, but that would be silly! What if we change the map around later on? Then it probably won't work and we have to go back and change every single one.
So here's an approach that's worked well for me. In each unique area, set its tag in the map editor to something that uniquely identifies it. Like "Level1Start" or "Level2Dungeon." Whatever. Now we know how to identify the destination when we teleport the player. But wait, how do we know which destination any particular teleport wants to go to? Easy, just add a new var to area/teleport called destination. It'll hold the tag of the destination area. You can set this in the map editor too.
So now we have:
area/teleport
var/destination
Entered(mob/m)
Now we're ready to fill in the Entered() proc. What do we want? Move the player (m) to the destination. We probably want Move instead of locate because if the destination is blocked (somebody else is standing there), the player shouldn't be able to go, right? But then again, maybe not in your game, so you'll have to decide which you use.
Here's what I'd do:
Entered(mob/m)
var/dest = locate(src.destination)
if (dest) // if no destination set in the map editor, don't do anything
m.Move(dest)
else
. = ..()
Now, in the map editor, you have to set the tag vars of all your possible teleport destinations. Then set the destination vars of all your teleport areas to be the tags of their actual destinations.
Example: on the right edge of Z level 1, I want players to go to the left edge of Z level 2, just as if it was continuous. So on the left edge of Z level 2, I set the tag vars to something like "Left2" in the map editor. Then on the right edge of Z level 1, I set the destination vars to "Left2". If I want players to be able to go back the same way, I do the opposite: set tags of right edge, Level 1 to "Right1" and destinations of left edge, level 2 to "Right1".
Easy!
- (Air Mapster)
mob
var/tmp
next_move_time = 0
movement_delay = 10
Move()
// If we've reached the next move time, allow the move.
if (world.time >= next_move_time)
next_move_time = world.time + 10
// Calls the regular Move() function to allow the move to happen.
return ..()
else
// Not allowed to move yet.
return 0
- (Nadrew)
There are three different procs that do this: rand(), prob(), and roll().
rand() is used to generate a random number between its first value and its second value. Eg.
var/num = rand(1,10)will set num to a random number between one and ten.
prob() is used to check a probability out of 100; it will return 1 if the check is true, or 0 if the check is false. For example,
prob(50)will have a 50% chance of returning 1 and a 50% chance of returning 0.
prob(25)will have but a 25% chance of returning 1 and a 75% chance of returning 0. This is useful for having an event occur based on a probability; eg.
mob/proc/myproc()
if(prob(25))
//do this
else
//do that
roll() is a tribute to traditional board-game RPGs of the latter part of the 20th century, where dice of multiple sides and configurations were rolled across a flat surface to, amazingly, generate random numbers! The format follows standard "die notation"... that is, "xdy+z". x is equal to the number of dice to roll, y is equal to the number of sides on those dice, and z is equal to the amount to add to the roll. For example, "2d6+4" will roll two six sided dice and add 4 to generate a number from 6 to 16. roll() can be called in two formats;
var/num1 = roll("2d6+4") //, or
var/num2 = roll(2,6)+4
The first uses die notation, while the second accomplishes the same thing, but in computer terms.
The BYOND reference rand() entry, http://www.byond.com/code/ref/info.html#/proc/rand
The BYOND reference prob() entry, http://www.byond.com/code/ref/info.html#/proc/prob
The BYOND reference roll() entry, http://www.byond.com/code/ref/info.html#/proc/roll
- (Spuzzum)
There are two variables a mob has that indicate whether it has a player or not. The first, mob.key, is used to determine the difference between an NPC and a PC. Unfortunately, mob.key does not guarantee that the player is actually logged in when you check it (though in most cases he or she will be).
mob/verb/check_player(mob/M)
if(M.key)
usr << "[M] is [M.key]'s PC."
else
usr << "[M] is an NPC."
The second, mob.client, points to the actual player, not his or her mob. The client contains special data about the player mostly relevant to real life, and not to the game. If you check mob.client, you will be guaranteed of knowing whether a player is logged in or not, but not whether it is an NPC or PC; a combination of both checks is required to gather both log-in status and whether it is a PC or NPC:
mob/verb/check_player(mob/M)
if(M.client && M.key) //if it has a client and has a key
usr << "[M] is [M.key]'s PC, and is logged in."
if(!M.client && M.key) //if it doesn't have a client but has a key
usr << "[M] is [M.key]'s PC, but is not logged in."
else //if it doesn't have a client and doesn't have a key
usr << "[M] is an NPC."
The BYOND guide discussion about keys, http://www.byond.com/code/guide/info.html#2.5
"The Connection Process" tutorial, http://www.byond.com/code/tutorial/connect/connect.html
The BYOND reference mob.key entry, http://www.byond.com/code/ref/info.html#/mob/var/key
The BYOND reference client entry, http://www.byond.com/code/ref/info.html#/client
- (Spuzzum)
7. When should I use the #include statement rather than simply clicking on the files I want in the file tree?
The only time you ever need to use #include is when you need to ensure that another file is processed before some other code that depends on it. Most DM code is order-independent, but when you override object procedures or variable initializations, the order does matter. When you do that, the code that defines the object should always be included first. The compiler does a pretty good job of detecting when you forget to do this and will issue warnings to that effect.
In the future, the graphical inclusion of files may be smarter about what order to read them in, at least reading libs before non-libs. But even then, one lib could depend on another and would need to ensure a certain order of processing. Currently, the files are simply processed in alphabetical order. (You can see the list by opening up the .dme file. It just makes a bunch of #includes for you.)
The BYOND guide discussion about preprocessor directives, http://www.byond.com/docs/guide/info.html#2.7.2
The BYOND reference include entry, http://www.byond.com/docs/ref/info.html#/DM/preprocessor/include
- (Dan of Dantom)
Whenever you have code that is hard to read, or if you have code that does something different than what it would look like at a very shallow glance, you need to add a comment. Likewise, if you do not understand the code you are using, put comments in to let yourself and others know that you aren't too sure.
Normally, you would not put comments that explain what the code does. Instead, when you put comments into code, you try to explain what that code is going to be used to do.
Examples:
mob.HP -= 5 //subtract 5 from mob's HP
That example is a bad comment. We can already tell that it subtracts 5 from the mob's HP var. What we would like to know is WHY it subtracts those 5 HP.
mob.HP -= 5 //usr is damaged from spell, so subtract 5 HP
That is a good comment, because it explains why you are using that code. The person reading the code knows (or should know) that it is subtracting 5 HP from the mob. What they need explained is the reason to subtract 5 HP, and it is given.
Don't add comments needlessly, either. If you think that someone can easily determine what a verb or proc does, no comments are necessary. For example:
world << "[usr.name] says, \"[T]\""
This line is fairly self-explanatory.
Always, always, always use comments if you don't understand it when you implement it, though! This aids you immensely if that code has an error, and it helps us immensely if you send us the code to debug.
- (Spuzzum)
Combat systems are one of the hardest concepts of BYOND, and the problem with them is that a combat system, in a MUD, essentially defines the game itself. Asking someone to write a combat system for your MUD is almost a completely ludicrous query.
On the whole, you need to take into consideration the following aspects;
How do I want players to attack enemies? By clicking? Double-clicking? Selecting orders? Verbs?
Is combat continuous? I.e. if I click on a monster, do I continue attacking it until it is dead? Do I have to keep telling it to attack?
How do I handle damage? Are there different forms of damage?
How will I level up? At which point? After how much experience?
Dan's NightSoil combat system, byond://Dan.NightSoil
Deadron's combat tutorial, byond://Deadron.CombatSystem
- (Spuzzum)
Copyright © 2001 by Ronald J. Hayden. Where an author is specified, the text they have provided is copyrighted by that author.
This material has been released under and is subject to the terms of the Common Documentation License, v.1.0, the terms of which are hereby incorporated by reference. Please obtain a copy of the License at http://www.opensource.apple.com/cdl/ and read it before using this material. Your use of this material signifies your agreement to the terms of the License.