diff --git a/doc/coding.txt b/doc/coding.txt index ce8088e..634692d 100644 --- a/doc/coding.txt +++ b/doc/coding.txt @@ -67,84 +67,85 @@ Contents When DikuMUD was first released in 1990, the authors were rightly more concerned with getting their product released than with little cosmetic details. Between writing 25,000 lines of C code and building the entire DikuMUD world, complete with -objects, rooms, and monsters, it’s understandable that making the code portable or +objects, rooms, and monsters, it’s understandable that making the code portable or clean was not at the top of the list of their priorities. Most DikuMUD distributions were not portable and had a number of bad bugs and even syntax errors which prevented the code from compiling at all. If a potential MUD implementor wanted to run a Diku, an excellent knowledge of C was necessary, because the MUD -simply wouldn’t run otherwise. +simply wouldn’t run otherwise. Now the situation is much different. With the proliferation of user-friendly -codebases any Average Joe can just type “make” and inflict yet another MUD on the +codebases any Average Joe can just type “make” and inflict yet another MUD on the world. Therefore, the number of truly unique MUDs as a fraction of the total is dropping drastically because coding experience is no longer a prerequisite to being -able to put a MUD up on the ‘Net. Some people may tell you that you don’t need to -know how to code in order to run a MUD – don’t believe them. Those people are +able to put a MUD up on the ‘Net. Some people may tell you that you don’t need to +know how to code in order to run a MUD – don’t believe them. Those people are wrong. -If you want your MUD to succeed and flourish, you’ll have to know how to code in C. -Otherwise, your MUD will be exactly like every other MUD out there. You’re not the -only person who knows how to type make! Although the quality and originality of your areas is also very important, it is the code and trigedit that transforms the -areas from lifeless text files into a living, breathing virtual world. If you don’t -know how to code, you won’t be able to add new features, respond to requests of +If you want your MUD to succeed and flourish, you’ll have to know how to code in C. +Otherwise, your MUD will be exactly like every other MUD out there. You’re not the +only person who knows how to type make! Although the quality and originality of your +areas is also very important, it is the code and trigedit that transforms the +areas from lifeless text files into a living, breathing virtual world. If you don’t +know how to code, you won’t be able to add new features, respond to requests of your players, add new world flags for your area builders, or even fix the simplest of bugs. Running a MUD without knowing how to code is certainly a recipe for -disaster. If you’re a great game-player and have some terrific ideas about how a -MUD should work, but don’t know how to code, you should either learn or find a good -coder who can join your team. Don’t assume that you can get away with running a MUD -without knowing C – you can’t. Not for very long, anyway. +disaster. If you’re a great game-player and have some terrific ideas about how a +MUD should work, but don’t know how to code, you should either learn or find a good +coder who can join your team. Don’t assume that you can get away with running a MUD +without knowing C – you can’t. Not for very long, anyway. -This document won’t teach you how to program in C; you’ll have to learn that on -your own. Instead, it will try to familiarize you with the way tbaMUD’s code is +This document won’t teach you how to program in C; you’ll have to learn that on +your own. Instead, it will try to familiarize you with the way tbaMUD’s code is structured so that you can put your C skills to good use. Even for the best -programmers, it takes a while to “get into” a program and feel comfortable enough +programmers, it takes a while to “get into” a program and feel comfortable enough with the way it works to start modifying it. Hopefully, by reading this manual, your breaking-in period for getting to know tbaMUD will be minimized. -tbaMUD consists of about 50,000 lines of moderately dense C code, so you shouldn’t +tbaMUD consists of about 50,000 lines of moderately dense C code, so you shouldn’t expect familiarity to come overnight. The best way to learn is to DO. Get your -hands dirty! Don’t be afraid to tinker with things. Start small by modifying +hands dirty! Don’t be afraid to tinker with things. Start small by modifying existing functions. Then, work your way up to creating new functions by copying old -ones. Eventually you’ll be able to write completely original functions, and even -tear some of tbaMUD’s native functions out as you realize completely new ways of +ones. Eventually you’ll be able to write completely original functions, and even +tear some of tbaMUD’s native functions out as you realize completely new ways of implementing them! But you should learn to walk before you try to run. Most of all, try to remember that coding for a MUD should be fun. It can sometimes be easy to lose site of the ultimate goal of personal enjoyment that MUDs are supposed to provide, particularly when they start to get crushed under the weight of their own politics or the egos of their administrators. If you enjoy coding, but -find yourself spending more time on politics than you are on code, don’t be afraid +find yourself spending more time on politics than you are on code, don’t be afraid to restructure your MUD or even remove yourself as Imp to a lower wizard position which requires less politics. A final thought: nothing will turn potential players away from your MUD more than -logging in and finding that it’s exactly like half the other tbaMUDs out there. +logging in and finding that it’s exactly like half the other tbaMUDs out there. Strive to show the world something new and unique. And may the source be with you. 2 Overview and Coding Basics Before getting down to the details of learning how to write code, we will first examine generally what a MUD is and what it does, to give you an overview of what -type of program you’re working with. +type of program you’re working with. -The first section, “An Internet Server Tutorial”, describes how Internet servers -such as tbaMUD work. It contains interesting background material if you’d like a +The first section, “An Internet Server Tutorial”, describes how Internet servers +such as tbaMUD work. It contains interesting background material if you’d like a deeper understanding of how the MUD actually interacts with the Internet and the -computer on which it runs, but little practical coding advice. So, if you’re +computer on which it runs, but little practical coding advice. So, if you’re reading this document purely to learn how to write MUD code, you should skip to the second section. 2.1 An Internet Server Tutorial -An Internet “server” is a program which provides some service to Internet users -(called “clients”). There are many different types of servers on the Internet. FTP +An Internet “server” is a program which provides some service to Internet users +(called “clients”). There are many different types of servers on the Internet. FTP servers allow you to transfer files between a remote computer and your own. Telnet servers allow you to connect to remote machines. News servers allow you to read USENET news. Similarly, tbaMUD is a server which allows you to play a game. However, MUDs such as tbaMUD differ from most Internet servers in several very important ways. When ten different people connect to tbaMUD, they can all interact -with one another. tbaMUD – a single program – must be aware of many users at the +with one another. tbaMUD – a single program – must be aware of many users at the same time. On the other hand, most other Internet servers such as FTP servers are written to only be aware of one user at a time. If more than one user wants to use an FTP server simultaneously, the operating system runs two copies of the server: @@ -158,7 +159,7 @@ makes the task of allowing users to communicate and interact with each other qui difficult. In addition, most simple Internet servers do not actually contain any network code -– the Internet superserver (inetd) contains most of the code to perform the network +– the Internet superserver (inetd) contains most of the code to perform the network magic, allowing the individual servers such as FTP and telnet to be network-unaware for the most part, simply reading from standard input and writing to standard output as if a user at a normal terminal was using the program. The Internet @@ -167,37 +168,38 @@ that they are actually network sockets and not a text terminal. To sum up, a MUD such as tbaMUD does not have the luxury of being able to handle multiple users by allowing the operating system to make many copies of the MUD. The -MUD itself must be capable of handling many users. The MUD also doesn’t have the +MUD itself must be capable of handling many users. The MUD also doesn’t have the luxury of allowing a pre-written program such as inetd to set up its network connections. The MUD itself is responsible for setting up and keeping track of all of its own network connections, as well as splitting its time evenly among all of -its players. The MUD cannot stop and wait for a player to type something – if +its players. The MUD cannot stop and wait for a player to type something – if it stops and waits for that player, the MUD will appear to have frozen from the point of view of all the other players! -Let’s make this idea more concrete with an example. Imagine that your first +Let’s make this idea more concrete with an example. Imagine that your first programming assignment in a C class is to write a simple calculator that gets two numbers from a user as input, multiplies them together, and then prints the product on the screen as output. Your program would probably be quite simple: it would -prompt the user for the two numbers, then stop and wait while the user types the numbers in. +prompt the user for the two numbers, then stop and wait while the user types the +numbers in. Now, imagine that your project is to write a program that lets 10 people simultaneously connect to your calculator and multiply their own numbers together. Forget for a moment the problem of how to write the network code that allows people to connect to your program remotely. There is a more fundamental problem here: your -original strategy of stopping and waiting for the user to type input won’t work any +original strategy of stopping and waiting for the user to type input won’t work any more. With one user, that worked fine. But what will happen with 10 users? -Let’s say your program stops and waits for the first user to type something. Now, +Let’s say your program stops and waits for the first user to type something. Now, what happens if the second user types something in the meantime? The program will not respond to the second user because it is still waiting for a response from the -first user. Your simple calculator has suddenly become much more complex – now, it +first user. Your simple calculator has suddenly become much more complex – now, it must constantly cycle through all users, asking the operating system if any one of them have typed something, without ever stopping to wait for a single user. When input comes in from any one of the users, your program must immediately process it and move on to the next user. -Let’s say that you’ve written a program which does the cycling among users +Let’s say that you’ve written a program which does the cycling among users described in the previous paragraph. Now, imagine that the operating system tells you that User 4 has just typed the number 12. You might be able to see the second problem: what does that 12 mean? Is 12 the first or second multiplicand for your @@ -207,51 +209,51 @@ it and wait for another number to multiply by 12? Your simple calculator has become more complicated again! Now, in addition to cycling through all users to check if any have typed anything, you must remember the STATE each user is in. In other words, each user might start out in a state -called “Waiting for First Number.” If a user types a number while she’s in the -“Waiting for First Number” state, you’d store her number somewhere and move her -into the “Waiting for Second Number” state. If she types a number while in the -“Waiting for Second Number” state, you’d retrieve the first number from memory, +called “Waiting for First Number.” If a user types a number while she’s in the +“Waiting for First Number” state, you’d store her number somewhere and move her +into the “Waiting for Second Number” state. If she types a number while in the +“Waiting for Second Number” state, you’d retrieve the first number from memory, multiply it by the number just typed, and print the result. Of course, each user -can be in a different state – there is no global state shared by all users. +can be in a different state – there is no global state shared by all users. -Now, you might be able to see how this calculator example relates to tbaMUD. Let’s -say that the MUD receives the string, “Sleep” from a user. What should tbaMUD do +Now, you might be able to see how this calculator example relates to tbaMUD. Let’s +say that the MUD receives the string, “Sleep” from a user. What should tbaMUD do with this string? Maybe the user is trying to log in, typing her name which happens -to be “Sleep”. Maybe the user is typing in her password. Maybe the user is already +to be “Sleep”. Maybe the user is typing in her password. Maybe the user is already logged in, and is trying to go to sleep! Just like with our calculator, the MUD -knows how to interpret data it receives from users by examining the users’ state. +knows how to interpret data it receives from users by examining the users’ state. -You can see a list of all possible players’ states in structs.h (they all start -with “CON_”). All users are put into the CON_GET_NAME state when they first connect +You can see a list of all possible players’ states in structs.h (they all start +with “CON_”). All users are put into the CON_GET_NAME state when they first connect to the MUD. CON_GET_NAME simply means that the MUD is waiting for the user to type -her name, at the “By what name do you wish to be known?” prompt. The normal state +her name, at the “By what name do you wish to be known?” prompt. The normal state that most players are in most of the time is the CON_PLAYING state, which indicates that they have already logged in and are playing normally. -Now, let’s go back to our previous example and trace exactly what happens when you -type “Sleep”. First, you type “Sleep.” Then, your computer sends the string “Sleep” +Now, let’s go back to our previous example and trace exactly what happens when you +type “Sleep”. First, you type “Sleep.” Then, your computer sends the string “Sleep” over the Internet to the computer on which the MUD is running. Within one tenth of a second, tbaMUD checks with the operating system to see if any of its users have -typed anything. When tbaMUD gets to you, it asks the operating system, “Has this -user typed anything?”. Since you typed “Sleep”, the operating system will respond, -“Yes!”. The MUD will then ask the operating system to deliver the message and will -read your message of “Sleep”. (All the magic of talking to the operating system and -checking to see whether or not you’ve typed anything happens in comm.c.) +typed anything. When tbaMUD gets to you, it asks the operating system, “Has this +user typed anything?”. Since you typed “Sleep”, the operating system will respond, +“Yes!”. The MUD will then ask the operating system to deliver the message and will +read your message of “Sleep”. (All the magic of talking to the operating system and +checking to see whether or not you’ve typed anything happens in comm.c.) -So, now that the MUD now knows that you’ve typed “Sleep”, it has to decide which of +So, now that the MUD now knows that you’ve typed “Sleep”, it has to decide which of several functions in interpreter.c should get control next. This depends on what -state you’re in. If you’re in the normal PLAYING state, it will pass control to a -function called command_interpreter, which will interpret “Sleep” as a normal -command and put you to sleep. If you’re in any other state, control goes to a +state you’re in. If you’re in the normal PLAYING state, it will pass control to a +function called command_interpreter, which will interpret “Sleep” as a normal +command and put you to sleep. If you’re in any other state, control goes to a function called nanny, which is responsible for handling all sockets in any state -other than PLAYING. nanny checks what state you’re in and acts accordingly. For -example, if you’re in the GET_NAME state, nanny activates the code to check whether +other than PLAYING. nanny checks what state you’re in and acts accordingly. For +example, if you’re in the GET_NAME state, nanny activates the code to check whether or not "Sleep" is the name of a known player (in which case it puts you into the -state asking for your password), or a new player (in which case it’ll ask you the -question, “Did I get that right, Sleep?”.) +state asking for your password), or a new player (in which case it’ll ask you the +question, “Did I get that right, Sleep?”.) -In a nutshell, that’s how tbaMUD interacts with the Internet. If you don’t -understand all the details, don’t worry – it’s not necessary to understand things +In a nutshell, that’s how tbaMUD interacts with the Internet. If you don’t +understand all the details, don’t worry – it’s not necessary to understand things on this level to be a successful MUD coder. If you are interested, however, there are some excellent references you can read for more information: @@ -263,37 +265,37 @@ Internet protocols are implemented by operating systems and is not as apropos to this discussion.) "Advanced Programming in the UNIX Environment" by Richard Stevens. An excellent -UNIX reference for the serious system programmer. Describes POSIX quite well – +UNIX reference for the serious system programmer. Describes POSIX quite well – worth its weight in gold for anyone trying to write portable UNIX applications. Sections on signal semantics and non-blocking I/O particularly apropos to Internet servers. -"UNIX Network Programming" by Richard Stevens. Similar to Volume 3 of Comer’s +"UNIX Network Programming" by Richard Stevens. Similar to Volume 3 of Comer’s series, but goes into more detail in several areas, and offers more practical code examples. -2.2 The Way Things Work – Overview +2.2 The Way Things Work – Overview 2.2.1 Boot Phase tbaMUD is a complex system of code, data files, and external input all interacting -in fun, unexpected ways. As with any program, it doesn’t just spring into existence +in fun, unexpected ways. As with any program, it doesn’t just spring into existence ready to play, but must cull information from the administrator and the system itself to determine how it should begin. The first action by tbaMUD on startup is to check for the existence of any command-line parameters, as seen in the main() function. These can have radical -impact on tbaMUD’s operation so it must check them before any other action. For +impact on tbaMUD’s operation so it must check them before any other action. For example, tbaMUD might be given the -d parameter, specifying an alternate library directory, so it cannot have done any processing on data files prior to the command-line reading. After finishing the immediate input, the next step is to be able to communicate to -the outside world. The communication may be either the “standard error” file +the outside world. The communication may be either the “standard error” file descriptor or a file, depending on the command-line options and whichever tbaMUD succeeds in opening. From here there are two possible branches depending on administrator input. If -“Syntax Check” mode is enabled, then we load only the world. Otherwise, we start +“Syntax Check” mode is enabled, then we load only the world. Otherwise, we start initializing the game in preparation for loading the world and accepting players. Since syntax checking is a subset of the normal startup phase, this document shall follow only the most common action of a non-syntaxcheck boot. @@ -302,7 +304,7 @@ A few minor items precede the loading of the world: initializing the random numb generator, creating the .killscript file, finding (or guessing) the maximum number of players the operating system will allow simultaneously, and opening the file descriptor later to be used to accept connections. This early opening of the -“mother” file descriptor is why there is a period of time during startup where a +“mother” file descriptor is why there is a period of time during startup where a player connection will be accepted but not receive a login prompt. tbaMUD does not check for connection attempts while it loads the world database but the operating system will complete the connection anyway and post notification to be seen later. @@ -315,12 +317,12 @@ immortal message of the day, mortal and immortal help default, message screen, general MUD info, wizard list, immortal list, policies, immortal handbook, background story, and login greeting screen. These files are reproduced verbatim by various user commands and exist for the players and/or administrators to read. The -MUD doesn’t interpret these in any way. +MUD doesn’t interpret these in any way. Next, the spell definitions are loaded for player use. The spello() function gives such important information as casting cost, valid targets (area, self, etc.), spell type, valid casting positions (sitting, standing, etc.), spell name, and wear off -message. Any skill or spell that isn’t set up via mag_assign_spells() will not be +message. Any skill or spell that isn’t set up via mag_assign_spells() will not be usable even if appropriate code exists elsewhere that would make it have an effect. Any spell defined here that does not have appropriate code elsewhere to handle it will do nothing when used. @@ -346,7 +348,8 @@ redirected to the mortal start room to allow booting to continue. The mortal sta room must exist in a room file loaded by lib/world/wld/index.mini file for mini-MUD mode to work. -Mobiles from lib/world/mob/index and objects from lib/world/obj/index are loaded afterward. Mobile and object virtual numbers need not correspond to the zone number +Mobiles from lib/world/mob/index and objects from lib/world/obj/index are loaded +afterward. Mobile and object virtual numbers need not correspond to the zone number ranges as rooms do but it is encouraged. There are various sanity checks done to mobiles and objects that may be printed during startup. Any warnings issued should be fixed but should not adversely affect the MUD itself. @@ -355,10 +358,11 @@ In the same manner as the room number virtual to real translation, the zone rese information is also translated. The zone reset structure contains a variety of different records so it takes special care to find the appropriate numbers to translate from virtual to real. Any virtual numbers that cannot be resolved result -in that zone command being disabled. Such entries have their type set to ‘*’ to +in that zone command being disabled. Such entries have their type set to ‘*’ to avoid the error in the future. -Triggers from lib/world/trg/index are loaded next. Trigger virtual numbers also do not need to correspond to the zone number ranges as rooms do but it is encouraged. +Triggers from lib/world/trg/index are loaded next. Trigger virtual numbers also +do not need to correspond to the zone number ranges as rooms do but it is encouraged. There are various sanity checks done that may be printed during startup. Any warnings issued should be fixed but should not adversely affect the MUD itself. @@ -374,17 +378,17 @@ appropriate entries after the boot. Even though the help index is stored by keyword, any entry having multiple keywords is only stored once for each set. An index of the player file is built to allow random access to each player as -attempt to connect or save. The index stores the player’s name, to search by for +attempt to connect or save. The index stores the player’s name, to search by for login, and their ID number, for the mail system to search by. The array index is their position in the player file, used for loading and saving. Fight messages and socials loaded next are placed by line in their appropriate -categories. The messages and socials themselves aren’t interpreted beyond their -placement but they’ll be used extensively in the game. Spells defined earlier from +categories. The messages and socials themselves aren’t interpreted beyond their +placement but they’ll be used extensively in the game. Spells defined earlier from mag_assign_spells()get their battle messages from this file. Defaults are provided for the battle messages if none is defined. -Special procedures must be associated with their now-loaded object so they’re +Special procedures must be associated with their now-loaded object so they’re processed now. A virtual number that cannot be resolved for the special procedure elicits a warning message on startup but a missing special procedure function will cause a compiler error. Shopkeepers are assigned via assign_the_shopkeepers() @@ -393,27 +397,28 @@ result of the shop files loaded earlier. Since the spells are skills were defined earlier, they can then be assigned to each class upon a certain level. The spells and skills given to the various classes only -depend upon a SPELL_ or SKILL_ definition so the assignment doesn’t need to care if +depend upon a SPELL_ or SKILL_ definition so the assignment doesn’t need to care if it is a generically handled spell or a custom implemented skill. The spells and skills may be assigned per the whims of the administrator based on their view of the appropriate classes. The command and spell tables must be sorted for special circumstances. In the case of the command table, it is for the commands input which displays all known -commands for that player’s level. The spell table is sorted for the practice +commands for that player’s level. The spell table is sorted for the practice command for easier visual searching by the player. Neither are critical to the -MUD’s operation but exist for the players’ benefit. +MUD’s operation but exist for the players’ benefit. The tbaMUD mail system saves MUD messages into the players file. It requires an index be built on startup to keep track of which blocks are free and which blocks have mail messages yet to be delivered. It is also important for the code to check -the mail file to make sure it hasn’t been corrupted somehow. A report on the number +the mail file to make sure it hasn’t been corrupted somehow. A report on the number of messages present in the system is printed when finished. The stupid-people prevention code of site banning and invalid name rejection comes next. The site ban code loads a text file list of all sites that have been deemed unworthy to connect to the MUD. Invalid name rejection loads a list of substrings -that must not appear in any character’s name that is being created. The invalid name list can be found at lib/misc/xnames. +that must not appear in any character’s name that is being created. The invalid name +list can be found at lib/misc/xnames. After deleting any expired rent files, the house code loads up any abodes defined. It must make sure the rooms still exist and the owner is still in the game before @@ -428,7 +433,7 @@ recorded after the zone reset to provide a display of the amount of time the MUD has been running. Once the world has finished being loaded, tbaMUD tells the operating system what -sort of signals it wants to receive and which to ignore. It doesn’t want to receive +sort of signals it wants to receive and which to ignore. It doesn’t want to receive a SIGPIPE, which would abort the program whenever it tried to write to a player who abruptly disconnected. The user-defined signals (Unix) SIGUSR1 and SIGUSR2 are set to re-read the wizard list file and unrestrict the game, respectively. SIGUSR1 is @@ -440,9 +445,9 @@ SIGINT are mapped to a function that prints their reception and then quits the program. The children signal, SIGCHLD, is set up to remove finished autowizinstances. -From here, the .killscript file is removed since if we’ve made it this far, we can +From here, the .killscript file is removed since if we’ve made it this far, we can start successfully. The only thing left now is to enter the interactive phase in -game_loop(), unless the “Syntax Check” option is enabled. +game_loop(), unless the “Syntax Check” option is enabled. 2.2.2 Interactive Phase @@ -457,19 +462,19 @@ is processed and commands run. This works to limit the number of possible comman the player can enter per second, such as speed-walking. If the game falls behind schedule, it will continuously process the pulses until it has caught up with where it is supposed to be. If over 30 seconds have passed, only 30 seconds are processed -as it would be computationally expensive to do them all. If there isn’t anyone -connected to the game at all, then the MUD sleeps until someone connects. (“If a -tree falls in the forest, and no one’s around to hear it...") +as it would be computationally expensive to do them all. If there isn’t anyone +connected to the game at all, then the MUD sleeps until someone connects. (“If a +tree falls in the forest, and no one’s around to hear it...") The first task of the pulse is to check for network socket input. First, any pending connections are given descriptors to track them. Then any descriptors with a socket in the exception set is kicked from the game. Incoming socket data is read next, checked for command history or repeat operations, and placed on the -appropriate descriptor’s command queue. +appropriate descriptor’s command queue. Having read commands, they are then set up to be executed. A player must first be in a condition to execute those commands, so anyone with a wait state is skipped -and people idled are pulled back into the game. Depending on the player’s +and people idled are pulled back into the game. Depending on the player’s activities, the input may be sent through either the message writing system, the text pager, the nanny() login sequence, the alias system, or straight to the in-game command interpreter. @@ -478,7 +483,7 @@ In the message writing system (see modify.c), any input, except for an /s at the beginning of a line, is simply appended to the string the character has decided to edit. An /s at the beginning of a line finishes the editing and returns the player to their previous state. Typical uses of this are using OLC, editing the -character’s description in the menu, or writing to boards and notes in the game. +character’s description in the menu, or writing to boards and notes in the game. The text pager allows the player to scroll around pages of text the MUD has produced. It allows refreshing the current page, going back a page, or continuing @@ -487,16 +492,16 @@ reaching the end of the text or the user telling it to quit. The nanny() login sequence guides the player through the initial authentication and entering of the game. Here they are prompted for the character name and password. -Upon successful login they may take actions such as changing their character’s +Upon successful login they may take actions such as changing their character’s description, changing their password, deleting the character, or entering the game. -The alias system provides a method for player’s to shortcut the typing of commands. +The alias system provides a method for player’s to shortcut the typing of commands. Each line of input is compared against their existing aliases and, if a match is found, the desired alias result is applied to their input and placed on the command queue. This process applies before the command interpreter so the game need not care what aliases each player may define. -Finally, the command interpreter pulls off a line of input from the player’s +Finally, the command interpreter pulls off a line of input from the player’s command queue. It uses the command table in interpreter.c to find the appropriate function, if any, to call for the given command. It does various checks to ensure the character is the proper level, in the correct position, and not frozen. Any @@ -505,8 +510,8 @@ command table. The special procedure may override the function completely, as th shopkeepers do with the buy and list commands, or allow it to execute. Processing the commands likely generated output for the player so the network -output is processed next. As much data is sent from the MUD’s output queue as will -fit in the operating system’s socket buffer for each player. Any output that can’t +output is processed next. As much data is sent from the MUD’s output queue as will +fit in the operating system’s socket buffer for each player. Any output that can’t fit in the socket buffer is held until the next pulse when perhaps some of the pending output will have been delivered. If any players decided to exit the game or otherwise disconnected, their descriptor is marked for removal and the connection @@ -516,7 +521,7 @@ Lastly, the periodic tasks are executed via the heartbeat() function. Each task run every minute, every 5 minutes, every pulse, or any other time increment. The list of tasks to run includes: -Process each zone’s timed updates. +Process each zone’s timed updates. Disconnect idle descriptors in the login sequence. Execute any triggers that are fired. Determine effects of violence. @@ -533,15 +538,15 @@ and could cause unpredictable behavior. 2.2.3 Shutting Down -The first responsibility on shutdown is to save the players’ characters to disk. +The first responsibility on shutdown is to save the players’ characters to disk. tbaMUD tracks which characters have been modified with a PLR_CRASH flag so it only needs to save those characters which have changed in the period from the last auto-save to the shutdown. To disconnect the network connections, each player socket is closed in turn. Closing their connection also frees up any memory associated but, unless memory -allocation tracing is enabled, it’s not necessary since we’re about to exit anyway. -The “mother” descriptor is closed last, preventing any new players from connecting. +allocation tracing is enabled, it’s not necessary since we’re about to exit anyway. +The “mother” descriptor is closed last, preventing any new players from connecting. Left to do are: closing the player files, saving the current MUD time for next startup, and logging normal termination of game. Saving the MUD time tries to @@ -556,16 +561,16 @@ may help prevent an ever-increasing memory usage while running. Memory tracing w vary depending on your operating system and may not necessarily be available on your particular platform. -That’s it, show’s over. “return 0;’’ +That’s it, show’s over. “return 0;’’ 2.3 Global Variables -tbaMUD doesn’t use objects in the sense of object-oriented languages so there are +tbaMUD doesn’t use objects in the sense of object-oriented languages so there are various global variables kept that must be manipulated in order to change the game world. Some are stored as arrays; others as lists. The global variables kept as arrays generally have an associated top_of_... function to denote its boundary. This is not an exhaustive list but the most frequently encountered of the ones in -use. A large number of global, constant strings are kept in constants.c but they’re +use. A large number of global, constant strings are kept in constants.c but they’re simply read from. Also see config.c for a number of configuration global variables. 2.3.1 World Variables @@ -605,7 +610,7 @@ here. There are top_of_zone_table + 1 zones in the array. 2.3.2 Object Instance Lists struct descriptor_data *descriptor_list; All players connected to the MUD have a -descriptor used to send the MUD’s output to and receive player input from. Each +descriptor used to send the MUD’s output to and receive player input from. Each descriptor does not necessarily have a character (not logged in yet). These are stored as a linked list of struct descriptor_data using the nextfield. @@ -626,7 +631,7 @@ same function with CMD_IS(). The size of the array is static and determined by t computer at the time of compilation. struct weather_data weather_info Raining? Snowing? Weather changes occurring in -weather.c are stored in this structure. The sun’s current state (dawn, dusk, etc.) +weather.c are stored in this structure. The sun’s current state (dawn, dusk, etc.) is kept here as well. struct time_info_data time_info The current date and time of the game world. Used @@ -670,8 +675,8 @@ bool is_number (const char *str) Tests if an entire string is an ASCII-encoded, decimal number by performing isdigit() on each character of the string. char *delete_doubledollar (char *string) The MUD, in processing input, converts a -single dollar sign to a double dollar sign. If you want to echo out a user’s input -through something other than act(), you will want to smash ’$$’ into ’$’ by using +single dollar sign to a double dollar sign. If you want to echo out a user’s input +through something other than act(), you will want to smash ’$$’ into ’$’ by using this function. 2.4.2 Argument Processing @@ -684,16 +689,17 @@ char *any_one_arg (char *argument, char *first_arg) These functions are frequent used in MUD commands to parse the arguments to those commands. As their names imply, one_argument() will peel off one argument from the string given by the user while two_arguments() will peel off two at a time. Note that these functions ignore -(and will not return) words such as: “in”, “from”, “with”, “the”, “on”, “at”, and -“to”. This is so the commands do not need to know the difference between "put the -sword in the bag" and “put sword bag”. If those words are really needed for the +(and will not return) words such as: “in”, “from”, “with”, “the”, “on”, “at”, and +“to”. This is so the commands do not need to know the difference between "put the +sword in the bag" and “put sword bag”. If those words are really needed for the command, then use any_one_arg() instead. It works just like one_argument() in all other respects. All of these functions convert the peeled off argument(s) to lower case as part of the process of storing them in the user-supplied buffer. char *one_word (char *argument, char *first_arg) Peels an argument off from a string like one_argument, but respects grouping via quoting. If the user supplies, -‘ "moby dick"’, one_argument() would return an argument of ’"moby’, while one_word() would return an argument of ‘moby dick’. This function converts the +‘ "moby dick"’, one_argument() would return an argument of ’"moby’, while one_word() +would return an argument of ‘moby dick’. This function converts the peeled off argument(s) to lower case as part of the process of storing them in the user-supplied buffer. @@ -704,10 +710,10 @@ the result (sans leading spaces) into a second buffer. 2.4.3 Character Output (Hello, world!) void log (const char *format, ...) Whenever a piece of information needs to be sent -to the MUD’s logs, this is the function to use. It is especially useful for +to the MUD’s logs, this is the function to use. It is especially useful for debugging and supports variable arguments like the printf() and sprintf() -functions. To prevent compilation errors due to a conflict with C’s natural -logarithm function of the same name, ‘log’ is actually an alias for this function’s +functions. To prevent compilation errors due to a conflict with C’s natural +logarithm function of the same name, ‘log’ is actually an alias for this function’s real name, basic_mud_log(). void mudlog (const char *str, int type, int level, bool file) In most cases @@ -715,17 +721,18 @@ mudlog()is better than log() because it announces to both the immortals on the M and, optionally, the file logs. Chances are the immortals will notice something faster while logged in to the game than in the system logs. -void send_to_char (struct char_data *ch, const char *messg, ...) This is the game’s +void send_to_char (struct char_data *ch, const char *messg, ...) This is the game’s tether to the players; its mouth; its voice. Most game output goes through this function so it is used very frequently. It supports variable argument formatting -like the C library’s printf() and sprintf()functions. +like the C library’s printf() and sprintf()functions. void act (const char *str, bool hide_invisible, struct char_data *ch, struct obj_data *obj, const void *vict_obj, int When dealing with character interactions there are frequently three situations to cover: the actor, the target, and the -observers. This function takes care of such output along with handy cases for his/her, he/she/it, and other language special cases. +observers. This function takes care of such output along with handy cases for his/her, +he/she/it, and other language special cases. -See the ’act()’ documentation (act.txt) for more information on what each +See the ’act()’ documentation (act.txt) for more information on what each particular parameter is used for. void page_string (struct descriptor_data *d, char *str, bool keep_internal) Places @@ -742,11 +749,12 @@ the first valid line is returned. A value of zero indicates that an error occurr or that end of file was reached before a valid line was read. int get_filename (char *orig_name, char *filename, int mode) Fills in the -’filename’ buffer with the name of a file of type ‘mode’ for a player with name -‘orig_name’. The mode parameter can be one of: +’filename’ buffer with the name of a file of type ‘mode’ for a player with name +‘orig_name’. The mode parameter can be one of: -CRASH_FILE, for player object files, PLR_FILE, for player files, The returned filename contains a path to a file in a directory based upon the file type and the -first letter of ‘orig_name’. +CRASH_FILE, for player object files, PLR_FILE, for player files, The returned filename +contains a path to a file in a directory based upon the file type and the first letter +of ‘orig_name’. 2.4.5 Utility Functions @@ -755,26 +763,26 @@ number generator in the range [from, to]. The random number generator is seeded with the time tbaMUD booted as returned by the time() system call. This provides a good, difficult to predict sequence of numbers. -int dice (int num, int size) Simulate rolling ‘num’ dice, each with ‘size’ sides, +int dice (int num, int size) Simulate rolling ‘num’ dice, each with ‘size’ sides, and return the sum of the rolls. size_t sprintbit (bitvector_t bitvector, const char *names[], char *result, size_t reslen) Treat an array of strings as if they were descriptions for the individual -bits in ‘bitvector’ and create a string describing it. This is used by the wizard +bits in ‘bitvector’ and create a string describing it. This is used by the wizard function do_stat() to give human-readable output to the various bitvectors used as -storage by the code. This is the approximate reverse of “PRF_LOG1 | PRF_DEAF”, for +storage by the code. This is the approximate reverse of “PRF_LOG1 | PRF_DEAF”, for example. size_t sprinttype (int type, const char *names[], char *result, size_t reslen) Retrieves a value from an array of strings. The difference between this and -“array[number]” is that this will avoid reading garbage values past the end of the -array. sprinttype() assumes the string arrays are terminated by a “\n” entry. +“array[number]” is that this will avoid reading garbage values past the end of the +array. sprinttype() assumes the string arrays are terminated by a “\n” entry. int search_block (char *arg, const char **list, int exact) Searches an array of -strings for a match to ‘arg’ and returns the index in the array of the match, or -1 -if not found. If ’exact’ is false, then a prefix match is done akin to is_abbrev(). +strings for a match to ‘arg’ and returns the index in the array of the match, or -1 +if not found. If ’exact’ is false, then a prefix match is done akin to is_abbrev(). This is useful to map symbolic names to numerical constants. Think of it as the -opposite of array[number]: “What index has this value?” +opposite of array[number]: “What index has this value?” 2.4.6 Character/Object Manipulation @@ -782,7 +790,7 @@ void char_to_room (struct char_data *ch, room_rnum room) void char_from_room (struct char_data *ch) Reciprocal, low-level functions to put a character into and remove a character from a given room. The room number must be -specified with a “real number” (an index into the room tables) as returned by +specified with a “real number” (an index into the room tables) as returned by real_room(). Since a character can only be in one room at a time, you must call char_from_room() @@ -802,7 +810,7 @@ void equip_char (struct char_data *ch, struct obj_data *obj, int pos) struct obj_data *unequip_char (struct char_data *ch, int pos) Takes a free-floating object (i.e., not equipped, in inventory, on the ground, or in another object) and equips it to the character for the specified location. unequip_char() does the -opposite; it removes the object from the character’s equipment list and returns it +opposite; it removes the object from the character’s equipment list and returns it as a free-floating object. The object being unequipped must be placed elsewhere or destroyed. Note that some objects may not be equipped by characters of certain classes and/or alignments. @@ -810,10 +818,10 @@ classes and/or alignments. void obj_to_char (struct obj_data *object, struct char_data *ch) void obj_from_char (struct obj_data *object) Reciprocal, low-level functions to put -an object into and remove an object from a given character’s inventory. Since an +an object into and remove an object from a given character’s inventory. Since an object can only be in one location at a time, you must use one of the obj_from_X() functions to remove it from its current location before using obj_to_char()to place -it in someone’s inventory. After an obj_from_char()call, the object is in NOWHERE +it in someone’s inventory. After an obj_from_char()call, the object is in NOWHERE and must be moved to another location using one of the obj_to_X() functions. These functions do not check if the character is allowed to carry or discard the @@ -854,94 +862,94 @@ world, then destroys it. 2.4.7 Object Locating struct obj_data *get_obj_in_list_num (int num, struct obj_data *list) Get an object -from ‘list’ with the specified real object number. Only takes first object; no -"2.bread” support. +from ‘list’ with the specified real object number. Only takes first object; no +"2.bread” support. struct obj_data *get_obj_num (obj_rnum nr); Find the first object in the world with -the real object number given. Does not have “2.” support. +the real object number given. Does not have “2.” support. struct obj_data *get_obj_in_list_vis (struct char_data *ch, char *name, int -*number, struct obj_data *list) Find the ‘number’-th object in ‘list’ with keyword -’name’ that the character can see. A NULL is returned on failure to locate such an -object, or if not enough objects to satisfy ‘number’ were found. ‘number’ is a +*number, struct obj_data *list) Find the ‘number’-th object in ‘list’ with keyword +’name’ that the character can see. A NULL is returned on failure to locate such an +object, or if not enough objects to satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such -as room then world. If the first object is desired, ‘number’ is left NULL. +as room then world. If the first object is desired, ‘number’ is left NULL. struct obj_data *get_obj_vis (struct char_data *ch, char *name, int *number) Find -the ‘number’th object in the world with keyword ‘name’ that the character can see. +the ‘number’th object in the world with keyword ‘name’ that the character can see. A NULL is returned on failure to locate such an object, or if not enough objects to -satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be +satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such as room then world. If the first -object is desired, ‘number’ is left NULL. +object is desired, ‘number’ is left NULL. struct obj_data *get_obj_in_equip_vis (struct char_data *ch, char *arg, int -*number, struct obj_data *equipment[]) Find the ‘number’-th object in the -character’s equipment list with keyword ‘name’ that the character can see. A NULL +*number, struct obj_data *equipment[]) Find the ‘number’-th object in the +character’s equipment list with keyword ‘name’ that the character can see. A NULL is returned on failure to locate such an object, or if not enough objects to -satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be +satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such as equipment then inventory. If the -first object is desired, ‘number’ is left NULL. +first object is desired, ‘number’ is left NULL. int get_obj_pos_in_equip_vis (struct char_data *ch, char *arg, int *number, struct -obj_data *equipment[]) Return the index of the ’number’-th object in the -character’s equipment list with keyword ’name’ that the character can see. A -1 is +obj_data *equipment[]) Return the index of the ’number’-th object in the +character’s equipment list with keyword ’name’ that the character can see. A -1 is returned on failure to locate such an object, or if not enough objects to satisfy -‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented +‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such as equipment then inventory. If the first object -is desired, ‘number’ is left NULL. +is desired, ‘number’ is left NULL. int generic_find (char *arg, bitvector_t bitvector, struct char_data *ch, struct -char_data **tar_ch, struct obj_data Searches any or all of the character’s +char_data **tar_ch, struct obj_data Searches any or all of the character’s equipment, inventory, current room, and world for an object with the keyword given -in ‘arg’. A 2nd or 3rd object is denoted in “2.object” notation. The function’s -return value specifies where the object was found, or 0, and the ‘tar_obj’ value is +in ‘arg’. A 2nd or 3rd object is denoted in “2.object” notation. The function’s +return value specifies where the object was found, or 0, and the ‘tar_obj’ value is updated with the object itself, or NULL. NOTE: This also does characters, either separately or simultaneously. 2.4.8 Character Locating struct char_data *get_char_room (char *name, int *number, room_rnum room); Find the -‘number’th character in the room with the keyword ‘name’. A NULL is returned on -failure to locate such a character, or if not enough characters to satisfy ‘number’ -were found. ‘number’ is a pointer to an integer so it can be decremented when doing +‘number’th character in the room with the keyword ‘name’. A NULL is returned on +failure to locate such a character, or if not enough characters to satisfy ‘number’ +were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such as room then world. If the first character is desired, -‘number’ is left NULL. +‘number’ is left NULL. struct char_data *get_char_num (mob_rnum nr); Find the first mobile in the world -with real mobile number given. This does not have support for “2.” notation. +with real mobile number given. This does not have support for “2.” notation. struct char_data *get_char_room_vis (struct char_data *ch, char *name, int -*number); Find the ‘number’-th character in the room with the keyword ‘name’ that +*number); Find the ‘number’-th character in the room with the keyword ‘name’ that is visible to the character given. A NULL is returned on failure to locate such a -character, or if not enough characters to satisfy ‘number’ were found. ‘number’ is +character, or if not enough characters to satisfy ‘number’ were found. ‘number’ is a pointer to an integer so it can be decremented when doing multiple searches, such -as room then world. If the first character is desired, ‘number’ is left NULL. +as room then world. If the first character is desired, ‘number’ is left NULL. struct char_data *get_char_world_vis (struct char_data *ch, char *name, int -*number); Find the ‘number’-th character in the world, searching the character’s -room first, with the keyword ‘name’ that is visible to the character given. A NULL +*number); Find the ‘number’-th character in the world, searching the character’s +room first, with the keyword ‘name’ that is visible to the character given. A NULL is returned on failure to locate such a character, or if not enough characters to -satisfy ‘number’ were found. ‘number’ is generally a pointer to an integer so it +satisfy ‘number’ were found. ‘number’ is generally a pointer to an integer so it can be decremented when this does both searches. If the first character is desired, -‘number’ is left NULL. +‘number’ is left NULL. struct char_data *get_char_vis (struct char_data *ch, char *name, int *number, int -where); When ‘{textttwhere’ is FIND_CHAR_WORLD, call ‘get_char_world_vis()’. -If‘where’ is FIND_CHAR_ROOM, call ‘get_char_room_vis()’. Otherwise, return NULL. +where); When ‘{textttwhere’ is FIND_CHAR_WORLD, call ‘get_char_world_vis()’. +If‘where’ is FIND_CHAR_ROOM, call ‘get_char_room_vis()’. Otherwise, return NULL. This is kept for compatibility with various calls in the source code or if people want to easily change a search based on a variable. int generic_find (char *arg, bitvector_t bitvector, struct char_data *ch, struct -char_data **tar_ch, struct obj_data); Searches the character’s current room and/or -world for a character with the keyword given in ‘arg’. A 2nd or 3rd character is -denoted in “2.character” notation. The function’s return value specifies where the -character was found, or 0, and the ‘tar_ch’ value is updated with the character +char_data **tar_ch, struct obj_data); Searches the character’s current room and/or +world for a character with the keyword given in ‘arg’. A 2nd or 3rd character is +denoted in “2.character” notation. The function’s return value specifies where the +character was found, or 0, and the ‘tar_ch’ value is updated with the character itself, or NULL. NOTE: This also does objects, either separately or simultaneously. 2.4.9 Violence void set_fighting (struct char_data *ch, struct char_data *victim); Initiates -fighting between ‘ch’ and ‘victim’. +fighting between ‘ch’ and ‘victim’. void stop_fighting (struct char_data *ch); Removes the character from a fighting posture. Note that if an enemy is still considered fighting this character, the @@ -950,12 +958,12 @@ character will revert back to fighting as soon as the enemy hits them again. void hit (struct char_data *ch, struct char_data *victim, int type); Makes the character attempt to hit the victim. The type determines if it is a skill, backstab in particular, or other type of damage to attempt to hit with. The type is -generally left as TYPE_UNDEFINED to use the character’s natural type. +generally left as TYPE_UNDEFINED to use the character’s natural type. int damage (struct char_data *ch, struct char_data *victim, int dam, int attacktype); Cause bodily harm to the victim, courtesy of the character. The damage and attacktype determine the message reported. Immortals and shopkeepers (that -aren’t charmed) may not be injured. Damage is capped at 100 per hit. +aren’t charmed) may not be injured. Damage is capped at 100 per hit. 3 Adding Features @@ -963,8 +971,8 @@ aren In the course of writing new functionality for your MUD, one of the first projects you may try is to add your own command. Most commands will require you to implement -some method to manipulate and parse the user’s input. In tbaMUD, this is done with -’command functions’ which have a special declaration form: +some method to manipulate and parse the user’s input. In tbaMUD, this is done with +’command functions’ which have a special declaration form: ACMD(do_/* Command name. */) @@ -975,9 +983,9 @@ ACMD(do_/* Command name. */) The command functions are then registered with the command interpreter by adding them to the cmd_info[] table in interpreter.c. The order within the command table is significant; entries at the top are substring matched prior to entries lower in -the list. So if ’kill’ is before ’kiss’, then ’ki’ will match ’kill’. Something -else to be aware of is that this can render commands unreachable, such as ’goad’ -being before ’go’. The’go’ can never be matched because ’goad’ will always have a +the list. So if ’kill’ is before ’kiss’, then ’ki’ will match ’kill’. Something +else to be aware of is that this can render commands unreachable, such as ’goad’ +being before ’go’. The’go’ can never be matched because ’goad’ will always have a valid substring matched first. The fields of importance are: @@ -987,7 +995,7 @@ substring matching when deciding upon the order in the table. const char *sort_as the minimum abbreviated command that will match. -byte minimum_position One of the POS_xxx constants #define’d in structs.h. This +byte minimum_position One of the POS_xxx constants #define’d in structs.h. This enforces the minimum position, inclusive, the user must be in, in order to execute the command. @@ -999,7 +1007,7 @@ sh int minimum_level The minimum level, inclusive, the user must be to execute t command. int subcmd To allow the same code function to handle multiple, similar commands, -this field allows an identifying number to be given to the command’s function. +this field allows an identifying number to be given to the command’s function. The ACMD declaration form is a C macro that sets up the command function to receive the right arguments. All command functions in tbaMUD receive the same set of @@ -1023,8 +1031,8 @@ purpose. int subcmd A special, user-defined integer value passed to select a "subcommand." Usually zero, but sometimes used when multiple commands with similar behavior are -implemented with a single command function. Since the subcmd’s value is supplied -within the command table and has a meaning determined entirely by the command’s +implemented with a single command function. Since the subcmd’s value is supplied +within the command table and has a meaning determined entirely by the command’s author, it will not change when you add new commands. A command with no arguments is very simple to write. For example, here is a simple @@ -1041,7 +1049,7 @@ as discussed before. This can be done by adding the ACMD prototype above cmd_inf ACMD(do_hello); -and then adding to cmd_info[]the command’s information, as previously discussed: +and then adding to cmd_info[]the command’s information, as previously discussed: { "hello", POS_DEAD, do_hello, 0, 0 }, @@ -1050,9 +1058,9 @@ Our information specifies this is a command named "hello" which anyone can use, regardless of their position (since dead is the minimum) or level (since 0 is the minimum), calls the do_hello() function to be executed, and has no subcmd. Note that because cmd_info[] does not encode information about the arguments to the -command, we don’t need to do anything different for commands that take arguments. +command, we don’t need to do anything different for commands that take arguments. -Since the command interpreter doesn’t process arguments for us (allowing +Since the command interpreter doesn’t process arguments for us (allowing cmd_info[] to be simple and general), we have to process them ourselves. Suppose we want to update our "hello" command to send a greeting to other users, when its invoked with an argument. First, we need to get the first argument (if any) by @@ -1062,13 +1070,13 @@ MAX_INPUT_LENGTH. (Note that regardless of what length we expect the argument to be, we always make our buffer the maximum input size, so users cannot overflow the buffer and crash the game.) -After this, we need to see what the argument is. If it is nothing, we’ll default to +After this, we need to see what the argument is. If it is nothing, we’ll default to our old behavior of just saying hello to our user. Otherwise, we need to look up -the character with the given name. If we can’t find anyone by that name, we send an +the character with the given name. If we can’t find anyone by that name, we send an error message. If we find someone, we send the greeting to the character we found. -We check if the given argument is empty by seeing if its first character is C’s end -of string marker (’0’ or 0). Since one_argument() strips leading spaces for us, we -don’t have to worry about them. If the string is not empty, we need to look up a +We check if the given argument is empty by seeing if its first character is C’s end +of string marker (’0’ or 0). Since one_argument() strips leading spaces for us, we +don’t have to worry about them. If the string is not empty, we need to look up a character in the current room by that name, using get_char_vis() (see Section 2.4.8. Character Locating). @@ -1082,7 +1090,7 @@ struct char_data *targ; /* Who to greet? */ one_argument(argument, arg); if (!*arg) { /* Common idiom for empty string test. */ act("Hello, $n.", FALSE, ch, NULL, NULL, TO_CHAR); - return; /* We’re done for this case. */ + return; /* We’re done for this case. */ } else if (!(targ = get_char_vis(ch, arg, FIND_CHAR_ROOM))) { send_to_char(ch, NOPERSON); @@ -1117,9 +1125,9 @@ tbaMUD improves greatly over standard Diku handling for spells, but how you go about adding them depends on the type of spell you want to make. Damage, affection, group, mass area, area, monster summoning, healing, status removal, and item enchanting spells are all generated in a template format with a touch of special -messages and coding effects. More complicated spells such as ’locate object’, -’summon’, or ’identify’ are a combination of the behavior of spells and commands. -They are spells in the sense the code checks for mana and requires the ’cast’ +messages and coding effects. More complicated spells such as ’locate object’, +’summon’, or ’identify’ are a combination of the behavior of spells and commands. +They are spells in the sense the code checks for mana and requires the ’cast’ syntax but are also commands in the sense that beyond the basic handling, the spell is implemented as a subroutine with given parameters. Visit http://tbamud.com for a tutorial on how to add spells. @@ -1131,7 +1139,7 @@ from mag_assign_spells() in spell_parser.c. It is called as: spello( unique_spell_number = ID# from 0 .. TOP_SPELL_DEFINE for this spell. -spell_name = Name to be used for ’cast’ command. max_mana = Mana cost of spell when +spell_name = Name to be used for ’cast’ command. max_mana = Mana cost of spell when first learned. min_mana = Minimum mana cost to ever cast the spell. @@ -1173,7 +1181,7 @@ very little different code. A damage spell is a damage spell is a damage spell. Even if a spell does multiple actions, such as blinding plus damage plus monster summon, the damage portion of the spell acts identical to a spell that simply does damage. The only difference is how much it does and whether there are special -mitigating factors. For example, ’chain lightning’ in mag_damage() (since it is a +mitigating factors. For example, ’chain lightning’ in mag_damage() (since it is a MAG_DAMAGE spell) is simply: case SPELL_CALL_LIGHTNING: @@ -1209,67 +1217,67 @@ to_vict = "You feel righteous."; break; Any modifier listed in structs.h in the APPLY_xxxsection may be used as the -location field. The modifier’s effect will depend on the affection type used. Up to +location field. The modifier’s effect will depend on the affection type used. Up to MAX_SPELL_AFFECTS values can be assigned to. Although not listed in the above -example, a ‘.bitvector’ value may be assigned to if the spell should tag the player +example, a ‘.bitvector’ value may be assigned to if the spell should tag the player with an AFF_ flag. If multiple castings of the same spell should be cumulative in -duration, the ‘accum_duration’ variable is set to TRUE. Likewise, if the modifier -is cumulative, the ‘accum_effect’ variable should be set to TRUE. A string -assigned to ‘to_room’ will be passed through act() for the occupants of the same -room as the caster. A ‘to_vict’ string will be given to act() with the target of +duration, the ‘accum_duration’ variable is set to TRUE. Likewise, if the modifier +is cumulative, the ‘accum_effect’ variable should be set to TRUE. A string +assigned to ‘to_room’ will be passed through act() for the occupants of the same +room as the caster. A ‘to_vict’ string will be given to act() with the target of the spell as the recipient of the message. Group spells simply call another spell on everyone in your current group. If you -want a ‘group fly’ spell, then you make a ‘fly’ spell first. Afterward, you make -the ‘group fly’ definition and then fill in some template areas of the perform_mag_groups() function. What you write there will depend on how your spell +want a ‘group fly’ spell, then you make a ‘fly’ spell first. Afterward, you make +the ‘group fly’ definition and then fill in some template areas of the perform_mag_groups() function. What you write there will depend on how your spell is designed. -General summoning spells (not ‘summon’ itself) deal with the conjuration of -mobiles. They require: ‘fmsg’, a failure message array index number; ‘mob_num’, the -virtual mobile number to summon; ‘pfail’, the percent chance of failure; and -‘handle_corpse’, mostly for the “animate dead” spell so it can move the items from +General summoning spells (not ‘summon’ itself) deal with the conjuration of +mobiles. They require: ‘fmsg’, a failure message array index number; ‘mob_num’, the +virtual mobile number to summon; ‘pfail’, the percent chance of failure; and +‘handle_corpse’, mostly for the “animate dead” spell so it can move the items from the corpse being animated to the mobile being summoned. These spells lend themselves to more customization than some of the other types. Healing spells in mag_points() can restore either health or movement points by -default. Just assign the amount of health healed to a ‘healing’ variable, the -amount of movement points restored to a ‘move’ variable, and send the target a -message with send_to_char(). The general code will handle updating the character’s +default. Just assign the amount of health healed to a ‘healing’ variable, the +amount of movement points restored to a ‘move’ variable, and send the target a +message with send_to_char(). The general code will handle updating the character’s attributes, position, and make sure a dying character is restored to normal functions if healed sufficiently. -Unaffection spells revert the effects of other spells, such as “blindness”, -“silence”, or “drunken stupor.” There are only three variables used in -mag_unaffects(): to_vict, to_room, and spell. The important variable is ‘spell’, +Unaffection spells revert the effects of other spells, such as “blindness”, +“silence”, or “drunken stupor.” There are only three variables used in +mag_unaffects(): to_vict, to_room, and spell. The important variable is ‘spell’, which determines which spell effect this unaffection spell will counter. The -‘to_vict’ and ‘to_room’ messages are optional but sent to the victim and room, +‘to_vict’ and ‘to_room’ messages are optional but sent to the victim and room, respectively, if provided. Object alteration spells deal with magical modifications to items, such as poisoning, cursing, enchanting, or making them invisible. These spells are all -unique by nature so only ‘to_char’ and ‘to_room’ are expected to be set, as -messages to the character and room, respectively. If ‘to_char’ is left NULL, it is -assumed the spell failed and a “no effect” message is given. +unique by nature so only ‘to_char’ and ‘to_room’ are expected to be set, as +messages to the character and room, respectively. If ‘to_char’ is left NULL, it is +assumed the spell failed and a “no effect” message is given. A creation spell conjures an item out of nothingness. The only variable expected is -‘z’, which specifies the object virtual number that should be created. Note that +‘z’, which specifies the object virtual number that should be created. Note that only a single object is created and there is no current mechanism for making multiples. The last function of note, mag_materials(), is not a spell type at all but a helper function which can be used to require up to 3 spell reagents for a particular spell to be cast. The function will return TRUE if the caster has the objects, otherwise -FALSE. If the ‘extract’ variable is TRUE, then the objects in question will be -consumed by the casting. You can also make the function ‘verbose’, but it is more +FALSE. If the ‘extract’ variable is TRUE, then the objects in question will be +consumed by the casting. You can also make the function ‘verbose’, but it is more of a debugging/funny option than practical. 3.3.2 Manual Spells -Any spell that doesn’t fit one of the template molds is implemented as a manual +Any spell that doesn’t fit one of the template molds is implemented as a manual spell. Adding a manual spell requires a function to be written, generally in spells.c, with the ASPELL() macro. After the requisite spell identifier macro is added to spells.h, add it to the manual spell list in spell_parser.c, call_magic(). -(Search for “MANUAL_SPELL”.) +(Search for “MANUAL_SPELL”.) Manual spells are given: @@ -1284,33 +1292,33 @@ victim: The target of the spell, if a character. obj: The target of the spell, if an object. Other than that, manual spells can do anything. Think of them as being similar to -standard commands in power and scope. A useful modification is to add ’argument’ -support to spells so that “locate object” works properly and a “change weather” -spell could make it “better” or “worse.” +standard commands in power and scope. A useful modification is to add ’argument’ +support to spells so that “locate object” works properly and a “change weather” +spell could make it “better” or “worse.” 3.4 Adding Skills Skills in tbaMUD are usually implemented as commands. The first steps to adding a skill are similar to those of adding a spell. First, make sure you have a clear -idea of what your skill is going to do, who you’re going to give it to, and how it +idea of what your skill is going to do, who you’re going to give it to, and how it fits in with the rest of the game. Try to avoid making too many skills that do -basically the same thing – having lots of skills isn’t a meaningful feature if most +basically the same thing – having lots of skills isn’t a meaningful feature if most of them can be ignored. After you have a good idea of what you want to do, why you want to do it, and why -it’s a good idea to do it, then start by adding a SKILL_xxx #define to spells.h and +it’s a good idea to do it, then start by adding a SKILL_xxx #define to spells.h and the corresponding skillo() line to mag_assign_spells() in spell_parser.c. The skillo() function takes, as its first argument, the SKILL_xxx #define and, as its second, the name of the skill, as a string. This registers the skill as something -that can be practiced. As with spells, you have to register the skill’s +that can be practiced. As with spells, you have to register the skill’s availability with individual classes at the appropriate levels in the init_spell_levels() function of class.c. Now your skill can be gained and practiced by players of an appropriate level and -class, but it doesn’t actually do anything. Most skills, like "bash" and "kick", +class, but it doesn’t actually do anything. Most skills, like "bash" and "kick", are simply commands that perform skill checks. The setup and everything else is the same as in Section 3.1, Adding Commands. The body needs to account for (1) whether -the command’s user can access the skill and (2) whether they were successful in +the command’s user can access the skill and (2) whether they were successful in using the skill. For (1), tbaMUD uses the idiom if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_xxx)) { @@ -1320,8 +1328,8 @@ if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_xxx)) { to check if the skill is available. The GET_SKILLmacro returns the proficiency (as a percentage) the given character has in the given skill. If the proficiency is 0%, -the player does not have the skill (either because his class doesn’t have it or -he’s not learned it, yet). This check is preferred over directly testing if the +the player does not have the skill (either because his class doesn’t have it or +he’s not learned it, yet). This check is preferred over directly testing if the player is of the right class(es) to use the skill, since that would require you to change several functions across several files to give skills to other classes (instead of just being able to add spell_level() calls in class.c). @@ -1330,7 +1338,7 @@ At this point you would do argument processing in the typical manner, as well as any other checks that are necessary (for instance, you might want to check if the room is peaceful, as done in do_bash() in act.offensive.c). Last, you want to check for the success or failure of the skill by rolling a percentage to compare against -the user’s proficiency (probability of success). This is typically done with: +the user’s proficiency (probability of success). This is typically done with: if (number(1, 101) > GET_SKILL(ch, SKILL_xxx)) { /* Failure. */ @@ -1338,7 +1346,7 @@ if (number(1, 101) > GET_SKILL(ch, SKILL_xxx)) { /* Success. */ } -where you’d replace the comments with the relevant failure or success code. +where you’d replace the comments with the relevant failure or success code. For skills that do damage, like "bash" and "kick", the messages for success and failure are typically not encoded in the skill itself, but instead as damage @@ -1371,7 +1379,7 @@ set to the SKILL_xxx #define, as in /* * See above and Section 3.1, Adding Commands: * ... skill checks, argument processing, etc. -* ... vict is set to skill’s victim. +* ... vict is set to skill’s victim. */ if (number(1, 101) > GET_SKILL(ch, SKILL_FOO)) { /* Failure means 0 damage is done. */ @@ -1382,15 +1390,15 @@ if (number(1, 101) > GET_SKILL(ch, SKILL_FOO)) { } Note that even when the skill succeeds and, thus, our call to do 10 damage to the -victim of the skill is made, we’re not guaranteed to do the damage. The hit may +victim of the skill is made, we’re not guaranteed to do the damage. The hit may miss, in which case damage() returns 0. Additionally, the hit may kill the victim, -in which case damage() returns -1. If we’re going to be modifying vict in our -skill’s function after the call to damage(), it’s important to take these return +in which case damage() returns -1. If we’re going to be modifying vict in our +skill’s function after the call to damage(), it’s important to take these return values into consideration. See do_bash() in act.offensive.c. 3.5 Adding Classes -Classes are one of the implementors’ most important decisions because the players +Classes are one of the implementors’ most important decisions because the players will constantly deal with them, their advantanges, and their limitations. A good class should be balanced so that it has its own unique perks and flaws, never making other classes pointless to play. @@ -1401,7 +1409,7 @@ characteristics are all there. The class needs a name, abbreviation, menu entry, unique class number, skill list, guild information, saving throws, combat hit probabilities, an ability priority list, hit/mana/move advancement per level, basic starting kit, opposing item flags, spells, skills, experience charts, and level -titles. It’s an exhaustive list, but the actual addition of a class isn’t nearly as +titles. It’s an exhaustive list, but the actual addition of a class isn’t nearly as complicated as it sounds. The first change for a class required external of class.c is in structs.h. There, @@ -1418,13 +1426,13 @@ search it for "ITEM_x (extra bits)". Add a string giving a short name for the ne ITEM_ANTI_xxx flag, in the appropriate order, before the "\n" entry near the bottom. -The shops have a similar "don’t trade" setup, so search shop.hfor "TRADE_NOGOOD" +The shops have a similar "don’t trade" setup, so search shop.hfor "TRADE_NOGOOD" to add a new TRADE_NOxxx item to the list for the class to be added. Below that -(near "NOTRADE_GOOD"), a line will need to be added for each new class so the ’no -trade’ status of a shop can be tested for the class. +(near "NOTRADE_GOOD"), a line will need to be added for each new class so the ’no +trade’ status of a shop can be tested for the class. With the definitions in shop.h, the shop code in shop.c can then be modified to -take into account the new classes. In a manner similar to constants.c, there’s a +take into account the new classes. In a manner similar to constants.c, there’s a table in shop.c with textual names for the TRADE_NOxxx values. Add the new class names to the "trade_letters[]" array in the same order as the TRADE_NOxxx bits were added to shop.h. Also in shop.c, the is_ok_char() function will need modified to @@ -1438,13 +1446,13 @@ Most of the changes to class.c will be straight-forward if going by the existing classes, so only a few items of note: 1. The letters used in parse_class() must be unique and should correspond to the -highlighted characters in the ’class_menu’ variable. +highlighted characters in the ’class_menu’ variable. 2. Lower saving throw values are better. -3. Lower ’thaco’ values are better. +3. Lower ’thaco’ values are better. 3.6 Adding Levels -Some people feel the standard 34 levels aren’t enough and want to add more. Others +Some people feel the standard 34 levels aren’t enough and want to add more. Others feel 34 is too many and want to reduce it. Fortunately, changing the number of levels in the MUD is fairly painless. There are only three important things to remember: adjusting the secondary tables to match your new levels, making over 127 @@ -1455,29 +1463,31 @@ The secondary functions that rely on levels directly are: saving_throws, thaco, backstab_mult, level_exp, title_male, and title_female. These must be changed to correctly cover the entire range of new levels for the MUD. If not, the missing levels will have incomplete data and may act in unexpected ways. Fortunately, -you’ll receive error messages in the logs if such an event happens. +you’ll receive error messages in the logs if such an event happens. As the number of mortals levels is always one less than the lowest immortal level, changing LVL_IMMORT in structs.hto a new value will give the desired change. Make sure you change the functions described above at the same time. The other immortals levels should be adjusted accordingly. -If you’re making more than 127 total levels on the MUD, a little structs.h surgery -is required. The default range on the ’level’ variable is -128 to 127. tbaMUD -doesn’t actually use negative levels so changing it to ’ubyte level’ will allow 255 -levels. Note that this setting hasn’t been tested so test your new level settings -to make sure they work as expected. If you need more than 255 levels, you’ll need -to change the ’byte’ to something larger, like ush_int (65,535) or unsigned int(4.2 +If you’re making more than 127 total levels on the MUD, a little structs.h surgery +is required. The default range on the ’level’ variable is -128 to 127. tbaMUD +doesn’t actually use negative levels so changing it to ’ubyte level’ will allow 255 +levels. Note that this setting hasn’t been tested so test your new level settings +to make sure they work as expected. If you need more than 255 levels, you’ll need +to change the ’byte’ to something larger, like ush_int (65,535) or unsigned int(4.2 billion). -Once you’ve changed the number of levels on your MUD, the implementor character you -may have already created will now have the wrong level to be an implementor. The first player to login with UID 1 by default has acces to the cheat command and can increase theri level to IMP. If you’ve decreased the levels then a ’set self level -XX’ command should work to drop yourself to the proper level, since you’re -considered above the new implementor level still. +Once you’ve changed the number of levels on your MUD, the implementor character you +may have already created will now have the wrong level to be an implementor. The first +player to login with UID 1 by default has acces to the cheat command and can increase +their level to IMP. If you’ve decreased the levels then a ’set self level XX’ command +should work to drop yourself to the proper level, since you’re considered above the +new implementor level still. Now remember to change all the mobiles too so they have proper levels. If you added -levels, it’ll make the mobiles weaker unless fixed. If reducing the levels, then -you’ll end up with error messages in the logs when those mobiles try to use saving +levels, it’ll make the mobiles weaker unless fixed. If reducing the levels, then +you’ll end up with error messages in the logs when those mobiles try to use saving throws or other level-dependent values. 3.7 Adding Color @@ -1488,7 +1498,7 @@ color off, no color, other than default, can be sent. To send color to the players, use the CC* family of macros: -CCNRM: Normal text color, as defined by player’s terminal. +CCNRM: Normal text color, as defined by player’s terminal. CCRED: Red CCGRN: Green CCYEL: Yellow @@ -1500,13 +1510,13 @@ CCWHT: White Each macro takes a pointer to the character and the level at which the color given should be displayed. If the player uses a lower level of color than given to the macro in the code, the color code will reduce to an empty string so it does not -appear. See ’color.txt’ for more information on this process. +appear. See ’color.txt’ for more information on this process. Now suppose you wish to add high-intensity colors, blinking, or backgrounds for your text. The place to look for the existing color codes is in screen.h, but -you’ll just see codes like "\x1B[31m" there. So what is "\x1B[31m"? It is an ANSI +you’ll just see codes like "\x1B[31m" there. So what is "\x1B[31m"? It is an ANSI color code understood by various terminal emulations to display color. There are -predefined colors for each code and a special format to use so you can’t just make +predefined colors for each code and a special format to use so you can’t just make up codes and expect them to work. In order to compare the low-intensity colors with the high-intensity, an additional @@ -1515,7 +1525,7 @@ color must be known to complete the pattern, black: #define BBLK "\\x1B[30m" The terminal background color is assumed black by tbaMUD so that particular color -definition isn’t in screen.h. Now a comparison of red and green with their bright +definition isn’t in screen.h. Now a comparison of red and green with their bright counterparts: #define KRED "\\x1B[31m" (Dark) @@ -1584,7 +1594,7 @@ of the three places and must therefore be prepared to react to all situations accordingly. In the next two sub-sections we will present both types of special procedures, and -in the third sub-section we’ll explain how to differentiate the three types of +in the third sub-section we’ll explain how to differentiate the three types of call. 4.2.1 Pulsed Special Procedures @@ -1604,8 +1614,8 @@ the Master Command Table. If it succeeds, before running the command associated the table, it checks for special procedures in the following order: room the player is in; -objects in the player’s equipment (does not enter the containers); -objects in the player’s inventory (does not enter the containers); +objects in the player’s equipment (does not enter the containers); +objects in the player’s inventory (does not enter the containers); mobiles in the room; objects in the room floor; @@ -1643,7 +1653,8 @@ the functions assign_mobiles, assign_objects, and assign_roomsin spec_assign.c. 4.4 The Special Procedure Function Header -The function header of any special procedure is defined in the macro SPECIAL() and is as follows: +The function header of any special procedure is defined in the macro SPECIAL() +and is as follows: int ()(struct char\_data *ch, void *me, int cmd, char *argument)