Cheezmud: a cheesy little mud in Objective C (GNU dialect) Copyright (C) 1995 David Flater. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. These are notes that I have made regarding things that are complicated or non-intuitive. They are not promised to be accurate in any way, and they certainly do not constitute complete documentation. How to Avoid Breaking the Mud ----------------------------- 1. Don't use really long names. Everywhere I use little 80-character buffers for constructing output. If you start using long names, the mud will self-destruct. 2. Be extremely careful with libobjects. If you ask it to do something tricky it will screw up the pointers and make cheezmud die mysteriously. 3. Never call free directly. Send the logout message instead. #2 explains why. 4. Don't delete or rename things without checking to see what the consequences might be. For example, the denizens of the Imperial Headquarters rely on the room names to decide how to move about. There are several other rooms that are referenced by name in the code, and if you do something to them, the mud will break. Types of Mud Objects -------------------- Players Monsters Rooms Do-Something Objects (Weapons, Magic Items, Containers, Tools) Do-Nothing Objects Money (Gold, Silver, Copper, Precious Stones, ...) NOT IMPLEMENTED YET How Actions Work ---------------- An action is a method that supplies an action to a player or other "active" object. Methods like describe and mudname are not actions. I just call them methods. When a player takes an action, a message must be sent to some object to cause the action to happen. Which object receives the message is determined by the priority system. For example, the "kill" action of a weapon has higher priority than the inherent "kill" action of the player (hand-to-hand combat). Both of these actions might in turn be overridden by a very high priority "kill" action provided by the room that prevents any killing from going on. Usually a mud has certain places where all fighting is forbidden, and this is how it is supported in cheezmud. This priority system is particularly useful in the case where a player picks up two items with conflicting goals -- for example, night vision goggles and a cursed object that makes you blind. Both of these affect the "look" action, but we probably don't want a blinded player to be able to see again simply by picking up the night vision goggles, so the cursed object gets higher priority. A more powerful magic that bestows super-vision might have higher priority than the cursed object. To support this priority system, each mud object must provide a method - (float) priority: (char *) action: (int) numargs; When this method is invoked, the return value is the priority of that action in the object (>= 0), or a negative number if that action is not supported by that object. Numerically higher priority values correspond to higher priority, unlike process priorities in Unix. The objects that are queried for potential player actions are the player, all objects directly in the player's inventory (not in a container), and the room the player is in. The same sort of resolution occurs for other nonrooms that call do, except that they might not be in a room -- they could be in somebody's inventory, in which case they can invoke the player's actions like room actions. (I don't recommend using this "feature".) In general, the priority of the "kill" action of a weapon should correspond to its deadliness, so that the most deadly weapon will always be used automatically if several weapons are being carried. The generic Weapon.m contains a heuristic to estimate a priority based on speed and damage, so if you don't tamper with the default methods it will set the priority for you. If you like, you can code the priority method to return a differet value for kill depending on the circumstances. A weapon that is special against a particular type of monster could sense the presence of that monster and increase its own priority. Hopefully it won't confuse players too much. The Sack class shows a cool use of variable priority. The bag action has priority based on how full the sack is, so that stuff will be distributed evenly among several sacks that are being carried. Not all commands are actions. Look at the ohce methods in Player.m and Mudadmin.m to see which ones are special. The mudadmin also has a special doadmin function that does not allow actions to be overridden and which does global object resolution instead of just the room and inventory. The Kill Action --------------- - kill: who: dobj; Kill dobj. This message is sent each heartbeat. It is the responsibility of the *weapon* to ignore 3 out of 4 to get a 1-second turnaround. This is so that some weapons can be faster than others. If you want to have a monster that has superhuman speed with an ordinary weapon, give it a high-priority kill method that is just like the weapon's kill method but faster. Then when somebody plunders the weapon it will still be an ordinary weapon. Some Special Methods -------------------- - (float) priority: (char *) action: (int) numargs; Return priority of an action. Actions take either one arg (who) or two args (who and dobj). Mud admin actions omit the who parameter. - describe: (char *) newname: (char *) newindef: (char *) newdef: (char *) newlongdesc; - setmudname: (char *) newname; - setindef: (char *) newdesc; - setdef: (char *) newdesc; - setlongdesc: (char *) newdesc; - (char *) mudname; - (char *) indef; - (char *) def; - (char *) longdesc; Methods to change or get the name and descriptions. Indef is the short description for a context wanting an indefinite article; def is the same thing for a definite context. Do not capitalize the first word unless it should always be capitalized -- Cheezmud will capitalize it if context dictates that it should be capitalized. The mudname is the keyword that must be used to refer to the object in commands. The long description is just that, with carriage returns hard-coded for an 80-column screen. Here are some examples of how to use mudname, indef, and def: mudname indef def ---------------------------------------------------------------- sword a sword the sword cord an honor cord the honor cord Frank Frank Frank sword the Sword of Shannara the Sword of Shannara Room names and player names should be unique to avoid dire consequences, like torching the wrong player. - (void) heartbeat; Every object receives this message four times per second. Inert objects ignore it. Most ert objects ignore it except once per second. The fast heartbeat is provided in case something really needs to move fast. The heartbeat will stabilize itself if the system clock takes a flying leap, but it will not attempt to "catch up" when it falls behind -- it will slow down gracefully. Catching up would be unfair to the players since they might get slaughtered without getting a chance to run when the heartbeat hurries up. - echo: (char *) text; The provided text is sent to a player's terminal or processed as needed by some other object. If an echo is sent to a room, the room sends the echo to every object in the room. - emote: emoter: (char *) verb_you: (char *) verb_he: (char *) emotion; - emote: emoter: (char *) verb_you: (char *) verb_he: dobj: (char *) emotion; Emote is similar to echo except that it handles 2nd vs. 3rd person description. It says "You look around" and "Name looks around" as appropriate. Emotes in a room are visible to everyone in the room. Emotes by objects in a player's inventory are only visible to that player. Emotes by objects inside of containers are not visible at all. - getlocation; - setlocation: whereto; - teleport: whereto; (Nonrooms) Getlocation can be used to find out where the object is. Setlocation silently changes that location. Teleport calls setlocation but also generates echoes indicating that the object has been magically moved. - (int) act: (char *) someaction; (Nonrooms) Perform an action. 1 is returned on success or if a diagnostic has been printed. 0 is returned on total failure. This method handles actions of both the 1 and 2 arg varieties. It resolves direct objects against the inventory and the room, except for get, drop, bag, and unbag which are special. - logout; This is the way of getting rid of something. Never call free directly. - hit: fromwho: (float) damage; Fromwho has hit you for that much damage. - clue: dontkillme; You are supposed to stop trying to kill the specified object. - (int) isdead; Things that are "dead" will shortly be deleted. - (float) health; - (float) stamina; Health is stamina over maxstamina. Stamina is just stamina.