Contents: -------- Disclaimer Credits Introduction Adding a New Race Deleting or Modifying Existing Races Understanding Classes Adding a New Class Deleting or Modifying Existing Classes Understanding Groups Adding a New Group Adding a New Skill or Spell Understanding Gsn's. Disclaimer ========== Although I believe the information in this document to be correct, it is by no means infallible or complete. By far more valuable than the entire rest of this paper is the advice "Back up your code!" and "Roll up your sleeves and dig in". The best way to learn anything is by doing it. In any case, use this information at your own risk. It's your code, your responsibility. Credits ======== For those of you who have not bothered to read the credits and license agreements for ROM, I recommend you do, they are located in the /doc directory and are called license.txt, license.doc, rom.license and rom.credits. My thanks to Rus Taylor for his efforts with ROM, and also to all of the original creators of Diku and Merc MUD. Thanks also to all of you on the ROM mailing list (rom@rom.org). heh, yeah, even you flamers... The sheer knowledge base available on that list has helped me immensely. A special thanks To Erwin Andreasen, who's snippets and documents for Merc and ROM mud have helped hundreds of mud admins and coders alike. It's peoplelike this, and thier seemingly tireless patience and willingness to help others that make the MUD community possible. ROM 2.4 is copyright 1993, 1996 by Russ Taylor. and is a deriviative of Merc Diku Mud. Merc Diku Mud is a derivative of the original Diku Mud and is subject to their copyright and license agreement. Merc Diku Mud contains substantial enhancements to Diku Mud. These enhancements are copyright 1992, 1993 by Michael Chastain, Michael Quan, and Mitchell Tse. Diku Mud is copyright (C) 1990, 1991 by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. Their license agreement is in the file 'license.doc'. Introduction ============= This file was written by Gary McNickle (gary@dharvest.com) at Dark Harvest Systems. The reason for it is simple, I've seen too many requests over the ROM mailing list of "how do I add a new race/class/skill???"... The process is fairly straight forward, but there are a few considerations to think of when removing or modifying races, classes, skills and groups that we'll cover here. This document should provide you with the instructions you need to add/delete/modify races for ROM 2.4x as well as WoT 1.x Keep in mind, that as these versions change, the procedures for adding races may change along with them... To begin, if you are not allready at least familiar with the basic concepts and verbage of programming in general, you should become so before you try to make even small changes... If you have a background in just about any programming language, then you should have no problem picking up this information... At the very least, you should be familiar with the different data types used in C programming. if you find any errors, or have any comments or suggestions, please send them via email to: gary@dharvest.com Adding a new race: =================== Adding a new race is actually quite simple... There are two primary types of races, "PC" and "NPC" or "Player Character" and "Non Player Character" races. Also, there are two different structures for races, the "race_type" structure, and the "pc_race_type" structure. ALL races, wether or not they are "PC" races, MUST be defined in the "race_type" structure. Only "PC" races need to be further defined in the "pc_race_type" structure. Let's cover the "race_type" first.. In the file "const.c" in your /src directory, find the data structure defined as: /* race table */ const struct race_type race_table [] = { { "human", TRUE, 0, 0, 0, 0, 0, 0, A|H|M|V, A|B|C|D|E|F|G|H|I|J|K }, { "elf", TRUE, 0, AFF_INFRARED, 0, 0, RES_CHARM, VULN_IRON, A|H|M|V, A|B|C|D|E|F|G|H|I|J|K }, See the two races, "human" and "elf"? Since it has data for most of the defined fields, let's take apart the "elf" and see what make's him tick... Line 1: "elf" -This is the name of the race TRUE -Tells us that this race is available for players to play. putting FALSE here would mean that noone can select this race during character creation. Line 2: 0 -The "act" or "action" flags for this race. In this example, there are none, so it's set to 0. see "ACT bits for MOBS" in the file "merc.h" for a complete listing of avialable action bits. NOTE: when adding flags, (or bits, as they are called) to any of the following fields, there may be times when you will want to add more than one (although, please think that through first). Say, for example, that instead of none (0), in this "act" field, you wanted your elf to be an aggressive (will attack player characters) thief. That's two "action" bits, you would add them like so: (replace '0' with:) AFF_AGGRESSIVE|AFF_THIEF notice the '|' seperating the two? You can add quite a few flags that way, certainly more than you should be able to... *grin* AFF_INFRARED -The "aff" or "affected by" flags for this race. in this example, "elves" are automatically affected by "infrared". This means that a player selecting this race is getting a new character that has "infrared" vision. See "bits for affected_by" in the file "merc.h" for a complete listing of available affect bits. 0 -The "off" or "offensive" flags for this race. These flags determine what "offensive" actions a MOB (NPC) of this race will have when created. See "OFF bits for mobiles" in the file "merc.h" for a complete listing of available offensive bits. (NOTE: YES, it's important that you pay attention to these bits, just because you may be creating a "player" race, does not mean that you shouldnt bother with defining these. This race can be used by your builders in area creation when they create MOBS as well, and this information will then be important) Line 3: These 3 items (set as: 0, RES_CHARM, VULN_IRON) determine, in order, the "imm", "res" and "vuln" flags of this race. Or, in english, "immunities, resistance and vulnerabilities". for a complete listing, see "IMM, RES and VUNL bits for mobiles" in the file "merc.h". Line 4: A|H|M|V These are the "form" bits for this race type, sepearted as mentioned earlier, with a '|'. Form bits determine things such as weather or not the race is edible, poisonous, undead, sentient, bipedal, mammal, etc.. See "body form" and "actual form" in "merc.h" for a complete listing. A|B|C|D|E|F|G|H|I|J|K This listing is the "parts" bits for your race. These determine things such as weather or not it has legs, eyes, a nose, ears, tails, claws, etc.. See "body parts" in "merc.h" for a complete listing. That's it for the "race_type" table, now, if your adding a new "PC" race type, it has a few further definitions that we need to add, these are put into the "pc_race_table". Look in "const.c" for the following definition: const struct pc_race_type pc_race_table [] = and again, let's break this down, line by line... Here's some examples from my mud... { "human", "Human", 0, { 100, 100, 100, 100 }, { "" }, { 13, 13, 13, 13, 13 }, { 18, 18, 18, 18, 18 }, SIZE_MEDIUM }, { "trolloc", "Trlck", 10, { 125, 125, 125, 100 }, { "detect good", "bash", "berserk", "second attack" }, { 16, 10, 12, 13, 15 }, { 23, 12, 14, 16, 23 }, SIZE_LARGE }, Let's break down the "Trolloc" race, since it's more fleshed out... Line 1: "trolloc" -This is the actual "name" of the race. This name MUST match with a name in the above "race_type" table. "Trlck" -This is the "Short name", displayed when someone does a "who" command. 10 -The number of creation points it costs the player if they select this race. Line 2: { 200, 300, 150, 100 }, -This is the "experience multiplier" for each class. This is used to determine experience points PER LEVEL, for this race, based on the class that the PC belongs to. Notice how "human" is all 100? That's because this number is divided by 100 in the function that determines experience, SO... T his is a percentage figure. The higher the number, the more experience points it will take for that particular class to gain a level. Now, the four positions here relate to the four classes that came with "stock ROM". (note: if you add classes, you'll also need to add to this data structure) and, "out of the box", go in order of, mage, cleric, thief, warrior) In this particular example, "Trollocs" are not very smart, and dont care about much more than killing, a Trolloc certainly would never be a cleric, so, it's twice as hard for a trolloc to be a Mage as it is to be a warrior, three times as hard to be a cleric, and, since they are too big to make good thieves, one and a half times as hard to be a thief... Line 3: { "detect good", "bash", "berserk", "second attack" }, -This array (skills[5]) determines what skills and spells the race is given at creation (you might say born with). Each of these must relate to a spell or skill previously defined in the "skill_table". In stock ROM, there is a pre-set limit of five skills that can be defined here. Line 4: { 16, 10, 12, 13, 15 }, -This array (stats[MAX_STATS]) determines the "minimum" stats that this race will start out with. The number of different stats is defined in "merc.h" as "MAX_STATS". The order these stats are listed in here is: "strength", "intelligence", "wisdom", "dexterity", "constiution" { 23, 12, 14, 16, 23 }, -This array (max_stats[MAX_STATS]) determines the "maximum" stats that this race can ever hope to achieve (unmodified, without special equipment or magic) and, like above, the number of different stats is defined in "merc.h" as "MAX_STATS". If you add a new stat type, you'll need to adjust that define. The order is the same as above. SIZE_LARGE =This determines the rough size of the race. There are three size definitions in stock ROM, "SIZE_SMALL", "SIZE_MEDIUM" and "SIZE_LARGE". As a reference, a human is typically SIZE_MEDIUM, where a horse would be "SIZE_LARGE" Well, that's it for races. Use the pre-defined race tables in "const.c" as a reference point, and you should have no problems. NOTE: once you add or even change a race, you should do a "clean" compile, in other words, delete all the .o files in the directory, and compile fresh. (you should do this any time you change any structure. There may also be source code modifications you might want to make for your new races, for examples of these, try doing a search in your /src directory for "human". (ie: grep -n human *.c) Deleting or Modifying existing races ==================================== Ahh... there's something to consider here. If, in your area files, any of the current races are allready being used then you will have problems if you delete those races from the source code. Also, you'll have problems if you change the "name" of a race that is being used in an area file... So, what do you do? Well, delete or modify all you want, just remember to do a search through all your area files (I recommend using 'grep') and make the necessary modifications in the affected files. Dont worry, ROM will certainly let you know if it finds a problem... Understanding Classes ===================== What is a "Class". Simple, for example; "Warriors" are a class all by themselves, as are "Thieves", "Mages", "Clerics". A class, quite simply, is a characters "job", what it does for a living. In ROM Classes are defined to allow the mud to distinguish between different types of players. In other words, Warriors and Mages for example, have different skills, where a Warrior has been trained in the arts of War, a Mage has been trained in the arts of Magic, and thier knowledge and skills should reflect this. SO, we use "classes" to set up these different "base" skills, what skills and knowledge a "level 1" warrior or mage should have before they set off to make their mark on the world. Also, Classes are used to set how difficult or easy it may be for a class to learn certain things. It's much more difficult for a warrior to cast a spell (or impossible!) than it is for a mage... Adding a new Class ================== The number of different classes you can have on your mud is determined by the define "MAX_CLASS" in the file "merc.h", so, adjust that define as the need arises. Find in "const.c", the line: const struct class_type class_table [MAX_CLASS] = { { "mage", "Mag", STAT_INT, OBJ_VNUM_SCHOOL_DAGGER, { 3018, 9618 }, 75, 20, 6, 6, 8, TRUE, "mage basics", "mage default" }, Now, as we did for races, let's break this down, line by line... Line 1: "mage", -This is the "name" of the class being defined. (*name) "Mag", -This is the "who_name" of the class, and is what is displayed when a player used the command "who". This name can be no more than three letters long. (who_name[4]) STAT_INT, -This is the primte attribute of the class. What is a "prime attribute"? Well, it's the one attribute that a member of this class is best at. This attribute will raise higher than others when a "mage" raises a level. The attributes available are: STAT_STR, STAT_INT, STAT_WIS, STAT_DEX, and STAT_CON. (attr_prime) OBJ_VNUM_SCHOOL_DAGGER, -This is the weapon that a member of this class is given when they are first created. Each player is given one weapon by the mud immediately after creation, this defines the "VNUM" (see "Virutal Numbers" in the ROM documentation) to use for this particular class, and MUST allready exist. Your mud will crash if it tries to find this vnum in an area file and it dosent exist! See "Well known object virtual numbers" in "merc.h" for a complete listing of weapon types that come shipped with stock ROM. (Note: if you want to add weapons of your own, you'll have to first create them in an area file, and then add thier define to merc.h) (weapon) Line 2: { 3018, 9618 }, -These define the two different rooms that lead to this class's GUILDMASTER, in stock ROM there are rooms for the various classes in Midgaard and New Thalos. (Thanks to Zain Dirkbane for this information.) 75, -The level at which, one reached, a player can no longer practice a skill. Typically 75. When a player spends a "practice" on a skill to raise his level of knowledge in that skill, this number is the limit that it can be raised to. Do your players a favor, and dont raise this past 75, make them earn the rest! (skill_adept) 20, -This is the "To Hit Armor Class of 0" In other words... When a player (or an npc for that matter) tries to hit someone else, a "dice roll" is made based on their skill, and thier thac0. A thac0 is defined as a minimum, so, this number is the minimum that a player must get on that dice roll to get a successful hit. There are two thac0's, one for low to mid level characters, and one for mid-high level. This is the first, low to mid level. (thac0_00) 6, -This is the "To Hit Armor Class of 0" for mid to high level characters. (thac0_32) 6, -The minimum amount of hit points that a character of this class will gain when they raise a level. (hp_min) 8, -The (hp_max) Maximum amount of hit points that a character of this class will gain when they raise a level. TRUE, -Whether or not this class gains any mana when they raise a level. (fMana) Line 3: "mage basics", -The "base group" that a character of this class is automatically given when they choose to modify their character during character creation. See "understanding groups" for further information. (base_group) "mage default" -The "default group" that a character of this class is automatically given when they choose NOT to modify their character during character creation. (default_group) That's it for the class_table, but there are a few other things to consider when adding new classes, just as with new races. For example; if you want to be able to set mobiles with this new classs, you'll have to add it to the act_flags table in tables.c. There may also be special code changes you will want/need to make within the code to help 'flesh out' your new class. do a search on the "warrior" class (in /src directory, "grep -n warrior *.c") to get an example of source code modifications that are class specific. Lastly, for each new class that you add, there are fields in the "skill_type" table that you will have to add to, as well as in the "group_type" table... Deleting or Modifying existing classes ====================================== As with races, when you delete or modify an existing class, there are many areas where trouble can happen if you dont make sure that you go through all the relevant source code and area files and make the required changes. Basically, for the source code, just go back through the steps to make a new class and be sure that any/all changed information is updated, but if, in your area files, any of your NPC MOBS are defined as one of these classes that have been deleted or modified, you'll have to make the changes in the area files as well. Also, be sure to inform your players when you make changes to classes or races or add new skills. Understanding groups ==================== There are two primary "groups" in ROM when it comes to classes and races. These were discussed briefly in "adding a new class". The two groups were talking about are the "base_group" and "default_group". A group definition is simply a categorized grouping of spells and skills. for example, you might have a "default elf" group that contains all the base skills and spells that members of the "elf" race are given when they are first created. There is an important distinction between a "base group" and a "default group". When a player creates a new character, they are given the option to "customize" their new character. If they say "Yes, I want to customize it!" then the "base group" is automatically added to thier characters list of skills and spells (at no cost to the character), and the "default group" is shown during customization as a "package deal" that they can usually purchase for less than if they were to buy each skill seperately. If they say "Naw... I trust you, I dont want to customize", then the "base group" is all that's given to them. ROM has defined "MAX_GROUP" as the maximum number of groups that you can have in your mud, you'll need to change that define if you go over that number. Adding a new group ================== Keep in mind, that the number of groups that you can have is defined in "merc.h" as "MAX_GROUP" Now, lets tear apart one of the stock ROM groups and see what's in it.. In "const.c", find the line: const struct group_type group_table [MAX_GROUP] = { { "warrior basics", { -1, -1, -1, 0 }, { "sword", "second attack" } }, Line 1: "warrior basics", -This is the name of this group. Simple, eh? { -1, -1, -1, 0 }, -This is the "rating" for each class (in order: mage, cleric, thief, warrior) What this does is determine how much this group costs the player in "creation points" when they select it, based on thier class. Now, "warrior basics" is a "base group" and it's only available to memeber of the warrior class, so the -1 set's this group as "not available" to mage, cleric, and thief classes, while the 0 means that it is available to warriros, and dosent cost them any creation points. (increase the 0, and you increase the cost) (rating[MAX_CLASS]) Line 2: { "sword", "second attack" } -This is a listing of the skills and spells that come with this group. The limit to how many you can have here is defined in "merc.h" as "MAX_IN_GROUP". Each of the spells/skills listed here, will be given to this group, at whatever cost is mentioned in the rating. (see above) (spells[MAX_IN_GROUP]) Now, the group we just looked at was a "base group", the costs in the rating are usually different if it's a "default group". Normally, the cost to the member of the specific class in a default group is 40, instead of 0. Remember, this is what the character is "suggested" to take as a minimum during customization. You can also have other groups.. here's some examples: { "weaponsmaster", { 40, 40, 40, 20 }, { "axe", "dagger", "flail", "mace", "polearm", "spear", "sword","whip" } }, { "attack", { -1, 5, -1, 8 }, { "demonfire", "dispel evil", "dispel good", "earthquake", "flamestrike", "heat metal", "ray of truth" } }, See how the costs vary? Take the "attack" group for example, mages are not allowed to purchase this group of spells, (-1), for clerics, it costs 5 creation points, thieves are also not allowed to purchase it, and for warriors, heh, it cost's a whopping 8 points! Adding a new skill or spell =========================== Adding new skills or spells can be as complex or simple as you want to make them. Fortunately for the creative portion of your brain, they require some actual coding on your part. You have to actually write what the new skill or spell does. It is beyond the scope of this document to instruct you on how to do that, however, we can help you to understand the internal structures of skills and spells for ROM. In "const.c" find the line: struct skill_type skill_table [MAX_SKILL] = here are defined all the skills available to players and mobs on your mud. You'll add all your new skills to this table. Let's take a look at the first skill (spell actually) that comes with stock ROM. { "acid blast", { 28, 53, 35, 42 }, { 1, 1, 2, 2}, spell_acid_blast, TAR_CHAR_OFFENSIVE, POS_FIGHTING, NULL, SLOT(70), 20, 12, "acid blast", "!Acid Blast!", "" }, Now, to break it down, line by line... Line 1: "acid blast", -The name of the skill (char *name) { 28, 53, 35, 42 }, -The levels needed (in order of class) by the various classes. As before, this order is Mage, Cleric, Thief, Warrior. (if you add new classes, you have to add to this field as well) In this example, "Acid Blast" is a level 28 mage spell, a level 53 (Immortal) cleric skill, a level 35 thief skill and a level 42 warrior skill. In other words, a warrior must be level 42 before he can practice or use this skill. (skill_level[MAX_CLASS]) { 1, 1, 2, 2}, -These determine how difficult it is for each class to learn this skill. A value of 0 or less here means that this class is unable to learn this skill. This also determines how many "training sessions" it will cost a member of that class to "gain" this skill. It's also the divisor used when practicing the skill. The lower the number (min 1) the better. It's harder for a class with a rating of 2 to practice or learn, than it is for a rating of 1. (rating[MAX_CLASS]) Line 2: spell_acid_blast, -This is the function that implements the skill, most spell functions are stored in "magic.c" and "magic2.c" while skills are found throught the source code. These skill functions take four arguments, an "sn" (see "understanding gsn's") a level, the caster and a target. I recommend looking at the spells in "magic.c" and "magic2.c" to get a solid grasp on how other's have gone about things before you. (*spell_fun) TAR_CHAR_OFFENSIVE, -The target of the skill. See "Target Types" in "merc.h" for a complete list, but here well discus the major ones: (target) TAR_CHAR_OFFENSIVE This skill is "offensive", you may be attacked for using it! TAR_CHAR_DEFENSIVE This skill is "defensive", no one will bother you for using it. TAR_CHAR_SELF This skill can only be used on yourself. TAR_CHAR_ROOM This skill get's used on the entire room! TAR_IGNORE The skill itself chooses it's target TAR_OBJ This skill affects objects only POS_FIGHTING, -This shows the minimum position the player can be in to use this skill. POS_FIGHTING for example, will allow the skill to be used if the player is either standing (POS_STANDING) or allready fighting. See "positions" in "merc.h" for a complete listing. (minimum_position) Line 3: NULL, -The address to an associated "gsn" (see "understanding gsn's") for this skill. Almost all skills have gsn's, few spells do. (*pgsn) SLOT(70), -The slot number. This field is out of date, and no longer used in ROM 2.4+ However, since it's still in the skill_table, you must put something here... *grin* Or remove it from the code (and all skills!) (slot) 20, -The minimum amount of mana that using this spell will cost. (min_mana) 12, -How long to pause the character when using this skill, in "Pulses". 4 pulses = 1 second. (beats) Line 4: "acid blast", -This is what is shown in the damage message when this skill is used, for example: "Your acid blast anhilates fido". (noun_damage) "!Acid Blast!" -The "wear off" message. This message is what's shown when the skill wears off. NOTE: If (like in this example) it's an instantaneous spell, put an "!" at the beginning and end of this message. This signifies an "error" message, and is very obvious when displayed. Also, skills (vs spells) dont use this field. (msg_off) "" -The wear off message for objects. As above, but applies to objects instead of characters. (msg_obj) For skills (vs spells), the following fields are not used; 'slot', 'min_mana', 'spell_fun', and 'target'. (the target is instead taken from the command line) Once you have written the skill itself, and added it (as we've just described) to the skill_table in const.c, you will also need to add it in two other places. Just as with any other command that the players can use, you have to add it's "do_function" to interp.h (or magic.h for spells) and add the command itself to the command table in "interp.c". Note: some skills are actually 'automatic', for an example of these skills, see "meditate", "fast healing", "parry", "dodge", "second attack" and others like them. (many are found in fight.c) Also, many of your skills will need gsns. These are added to db.c and merc.h Now, about gsns.... Understanding gsn's. ==================== GSN, or "Global Skill Number" are nothing more than a reference number for ROM. They are defined as follows: /* in db.c, the gsn is set like so: */ sh_int gsn_dodge; /* in merc.h, an extern gsn is set as: */ extern sh_int gsn_dodge; For skills that require gsn's, you have to add both the db.c gsn, and the extern version in merc.h. How do you determine if your skill needs a gsn? Simple, will it be used often, by a lot of people? If so, then it should have a gsn. Also, if it's an "automatic" skill, then by all means it should have one. GSN's are used to speed up the search for the skill in the database, skills without them take longer to search for than skills that have one. It's that simple... Now, it seems to me that a sorted linked list would be an even faster way to search, I'll be adding that to my ROM code soon and let you know how it works out...