From: Daniel Houghton Subject: Multiclassing: My approach Since I've already mentioned my experimental approach to multiclassing based on skills which define a character's effective level in a given class, I figured I may as well post a patchfile as a reference to anyone who'd like to try such an approach. Going with this approach will force you to edit the code further at various points for things such as guildguards, class checks, etc... but to me, it seems more elegant than other approaches I've seen. Before you start, realize that this is a patch derived from my own customized CircleMUD 3.0 beta 11, and as such WON'T work as a drop-in patch. DO NOT apply this to your MUD without backing things up first, and as per the standard disclaimer, I'm in no way responsible or liable for whatever may happen as a result of this patch. What this patch does NOT do is... A) Establish a system for advancing in multiple classes. I had to test it by using the implementor "skillset" command on the skills corresponding to the classes. B) Allow skill dependency on multiple skills, which unfortunately you can't have a spell castable by both mages and clerics. Since this code hasn't been developed fully, I haven't bothered - but it should be ridiculously easy to fix this. Quite simply, this is mailer code. But it should demonstrate the feasibility of using skills to effectively multiclass or dual-class. Have fun! Daniel Houghton aka Garadon ------ Diff File ------ diff -bcN -C 3 --exclude=*.o --exclude=*.rej --exclude=*.orig --exclude=*~ tlo/ class.c tlo_multiclass/class.c *** tlo/class.c Sat Mar 29 20:16:42 1997 --- tlo_multiclass/class.c Sat Mar 29 19:17:17 1997 *************** *** 453,552 **** - - /* - * SPELLS AND SKILLS. This area defines which spells are assigned to - * which classes, and the minimum level the character must be to use - * the spell or skill. - */ - void init_spell_levels(void) - { - /* MAGES */ - spell_level(SPELL_MAGIC_MISSILE, CLASS_MAGIC_USER, 1); - spell_level(SPELL_CREATE_LIGHT, CLASS_MAGIC_USER, 1); - spell_level(SPELL_DETECT_INVIS, CLASS_MAGIC_USER, 2); - spell_level(SPELL_DETECT_MAGIC, CLASS_MAGIC_USER, 2); - spell_level(SPELL_CHILL_TOUCH, CLASS_MAGIC_USER, 3); - spell_level(SPELL_INFRAVISION, CLASS_MAGIC_USER, 3); - spell_level(SPELL_INVISIBLE, CLASS_MAGIC_USER, 4); - spell_level(SPELL_ARMOR, CLASS_MAGIC_USER, 4); - spell_level(SPELL_BURNING_HANDS, CLASS_MAGIC_USER, 5); - spell_level(SPELL_LOCATE_OBJECT, CLASS_MAGIC_USER, 6); - spell_level(SPELL_STRENGTH, CLASS_MAGIC_USER, 6); - spell_level(SPELL_FAERIE_FIRE, CLASS_MAGIC_USER, 7); - spell_level(SPELL_SHOCKING_GRASP, CLASS_MAGIC_USER, 7); - spell_level(SPELL_SLEEP, CLASS_MAGIC_USER, 8); - spell_level(SPELL_LIGHTNING_BOLT, CLASS_MAGIC_USER, 9); - spell_level(SPELL_BLINDNESS, CLASS_MAGIC_USER, 9); - spell_level(SPELL_DETECT_POISON, CLASS_MAGIC_USER, 10); - spell_level(SPELL_LEVITATE, CLASS_MAGIC_USER, 10); - spell_level(SPELL_COLOR_SPRAY, CLASS_MAGIC_USER, 11); - spell_level(SPELL_ENERGY_DRAIN, CLASS_MAGIC_USER, 12); - spell_level(SPELL_CURSE, CLASS_MAGIC_USER, 13); - spell_level(SPELL_FIREBALL, CLASS_MAGIC_USER, 14); - spell_level(SPELL_STONESKIN, CLASS_MAGIC_USER, 15); - spell_level(SPELL_CHARM, CLASS_MAGIC_USER, 16); - spell_level(SPELL_HASTE, CLASS_MAGIC_USER, 17); - spell_level(SPELL_ICE_STORM, CLASS_MAGIC_USER, 19); - spell_level(SPELL_FIRE_SHIELD, CLASS_MAGIC_USER, 22); - spell_level(SPELL_INFERNO, CLASS_MAGIC_USER, 24); - spell_level(SPELL_ENCHANT_WEAPON, CLASS_MAGIC_USER, 26); - - - /* CLERICS */ - spell_level(SPELL_CURE_LIGHT, CLASS_CLERIC, 1); - spell_level(SPELL_REFRESH, CLASS_CLERIC, 1); - spell_level(SPELL_ARMOR, CLASS_CLERIC, 1); - spell_level(SPELL_CREATE_FOOD, CLASS_CLERIC, 2); - spell_level(SPELL_CREATE_WATER, CLASS_CLERIC, 2); - spell_level(SPELL_CAUSE_LIGHT, CLASS_CLERIC, 3); - spell_level(SPELL_DETECT_POISON, CLASS_CLERIC, 3); - spell_level(SPELL_DETECT_ALIGN, CLASS_CLERIC, 4); - spell_level(SPELL_CURE_BLIND, CLASS_CLERIC, 4); - spell_level(SPELL_CREATE_LIGHT, CLASS_CLERIC, 4); - spell_level(SPELL_BLESS, CLASS_CLERIC, 5); - spell_level(SPELL_DETECT_INVIS, CLASS_CLERIC, 6); - spell_level(SPELL_BLINDNESS, CLASS_CLERIC, 6); - spell_level(SPELL_INFRAVISION, CLASS_CLERIC, 7); - spell_level(SPELL_PROT_FROM_EVIL, CLASS_CLERIC, 8); - spell_level(SPELL_GROUP_ARMOR, CLASS_CLERIC, 9); - spell_level(SPELL_CURE_CRITIC, CLASS_CLERIC, 9); - spell_level(SPELL_SUMMON, CLASS_CLERIC, 10); - spell_level(SPELL_REMOVE_POISON, CLASS_CLERIC, 10); - spell_level(SPELL_CAUSE_CRITIC, CLASS_CLERIC, 11); - spell_level(SPELL_WORD_OF_RECALL, CLASS_CLERIC, 12); - spell_level(SPELL_EARTHQUAKE, CLASS_CLERIC, 12); - spell_level(SPELL_DISPEL_EVIL, CLASS_CLERIC, 14); - spell_level(SPELL_DISPEL_GOOD, CLASS_CLERIC, 14); - spell_level(SPELL_SANCTUARY, CLASS_CLERIC, 15); - spell_level(SPELL_CALL_LIGHTNING, CLASS_CLERIC, 15); - spell_level(SPELL_HEAL, CLASS_CLERIC, 16); - spell_level(SPELL_CONTROL_WEATHER, CLASS_CLERIC, 17); - spell_level(SPELL_HARM, CLASS_CLERIC, 19); - spell_level(SPELL_GROUP_HEAL, CLASS_CLERIC, 22); - spell_level(SPELL_REMOVE_CURSE, CLASS_CLERIC, 24); - spell_level(SPELL_BLADE_BARRIER, CLASS_CLERIC, 26); - spell_level(SPELL_HOLY_WORD, CLASS_CLERIC, 28); - - - /* THIEVES */ - spell_level(SKILL_SNEAK, CLASS_THIEF, 1); - spell_level(SKILL_PICK_LOCK, CLASS_THIEF, 2); - spell_level(SKILL_BACKSTAB, CLASS_THIEF, 3); - spell_level(SKILL_STEAL, CLASS_THIEF, 4); - spell_level(SKILL_HIDE, CLASS_THIEF, 5); - spell_level(SKILL_TRACK, CLASS_THIEF, 6); - - - /* WARRIORS */ - spell_level(SKILL_KICK, CLASS_WARRIOR, 1); - spell_level(SKILL_RESCUE, CLASS_WARRIOR, 3); - spell_level(SKILL_TRACK, CLASS_WARRIOR, 9); - spell_level(SKILL_BASH, CLASS_WARRIOR, 12); - spell_level(SKILL_SECOND_ATTACK, CLASS_WARRIOR, 15); - } - - /* Names of class/levels and exp required for each level const struct title_type titles[NUM_CLASSES][LVL_IMPL + 1] = { --- 453,458 ---- diff -bcN -C 3 --exclude=*.o --exclude=*.rej --exclude=*.orig --exclude=*~ tlo/ spec_procs.c tlo_multiclass/spec_procs.c *** tlo/spec_procs.c Sat Mar 29 20:16:42 1997 --- tlo_multiclass/spec_procs.c Sat Mar 29 19:14:53 1997 *************** *** 168,174 **** strcat(buf2, "**OVERFLOW**\r\n"); break; } ! if (GET_LEVEL(ch) >= spell_info[i].min_level[(int) GET_CLASS(ch)]) { sprintf(buf, "%-20s %-15s%s", spells[i], how_good(GET_SKILL(ch, i)), (!(++practwocolumn % 2) ? "\r\n" : "\t")); --- 168,175 ---- strcat(buf2, "**OVERFLOW**\r\n"); break; } ! /* if (GET_LEVEL(ch) >= spell_info[i].min_level[(int) GET_CLASS(ch)]) { */ ! if (GET_SKILL(ch, spell_info[i].base_skill) >= spell_info[i].min_level) { sprintf(buf, "%-20s %-15s%s", spells[i], how_good(GET_SKILL(ch, i)), (!(++practwocolumn % 2) ? "\r\n" : "\t")); *************** *** 209,216 **** skill_num = find_skill_num(argument); ! if (skill_num < 1 || ! GET_LEVEL(ch) < spell_info[skill_num].min_level[(int) GET_CLASS(ch)]) { sprintf(buf, "You do not know of that %s.\r\n", SPLSKL(ch)); send_to_char(buf, ch); return 1; --- 210,219 ---- skill_num = find_skill_num(argument); ! /* if (skill_num < 1 || ! GET_LEVEL(ch) < spell_info[skill_num].min_level[(int) GET_CLASS(ch)]) { */ ! if ((skill_num < 1) || ! (GET_SKILL(ch, spell_info[skill_num].base_skill) < spell_info[skill_nu m].min_level)) { sprintf(buf, "You do not know of that %s.\r\n", SPLSKL(ch)); send_to_char(buf, ch); return 1; diff -bcN -C 3 --exclude=*.o --exclude=*.rej --exclude=*.orig --exclude=*~ tlo/ spell_parser.c tlo_multiclass/spell_parser.c *** tlo/spell_parser.c Sat Mar 29 20:16:42 1997 --- tlo_multiclass/spell_parser.c Sat Mar 29 19:53:20 1997 *************** *** 207,213 **** "!UNUSED466!", "!UNUSED467!", "!UNUSED468!", "!UNUSED469!", "!UNUSED470!", /* 470 */ "!UNUSED471!", "!UNUSED472!", "!UNUSED473!", "!UNUSED474!", "!UNUSED475!", /* 475 */ "!UNUSED476!", "!UNUSED477!", "!UNUSED478!", "!UNUSED479!", "!UNUSED480!", /* 480 */ ! "!UNUSED481!", "!UNUSED482!", "!UNUSED483!", "!UNUSED484!", "!UNUSED485!", /* 485 */ "!UNUSED486!", "!UNUSED487!", "!UNUSED488!", "!UNUSED489!", "!UNUSED490!", /* 490 */ "!UNUSED491!", "!UNUSED492!", "!UNUSED493!", "!UNUSED494!", "!UNUSED495!", /* 495 */ "!UNUSED496!", "!UNUSED497!", "!UNUSED498!", "!UNUSED499!", "!UNUSED500!", /* 500 */ --- 207,221 ---- "!UNUSED466!", "!UNUSED467!", "!UNUSED468!", "!UNUSED469!", "!UNUSED470!", /* 470 */ "!UNUSED471!", "!UNUSED472!", "!UNUSED473!", "!UNUSED474!", "!UNUSED475!", /* 475 */ "!UNUSED476!", "!UNUSED477!", "!UNUSED478!", "!UNUSED479!", "!UNUSED480!", /* 480 */ ! ! /* CLASS TYPES */ ! ! "Magic Theory", ! "Priestly Ordination", ! "Thiefcraft", ! "Way of the Warrior", ! ! "!UNUSED485!", /* 485 */ "!UNUSED486!", "!UNUSED487!", "!UNUSED488!", "!UNUSED489!", "!UNUSED490!", /* 490 */ "!UNUSED491!", "!UNUSED492!", "!UNUSED493!", "!UNUSED494!", "!UNUSED495!", /* 495 */ "!UNUSED496!", "!UNUSED497!", "!UNUSED498!", "!UNUSED499!", "!UNUSED500!", /* 500 */ *************** *** 319,326 **** { int mana; ! mana = MAX(SINFO.mana_max - (SINFO.mana_change * (GET_LEVEL(ch) - SINFO.min_level[(int) GET_CLASS(ch)])), SINFO.mana_min); return mana; --- 327,337 ---- { int mana; ! /* mana = MAX(SINFO.mana_max - (SINFO.mana_change * (GET_LEVEL(ch) - SINFO.min_level[(int) GET_CLASS(ch)])), + SINFO.mana_min); */ + mana = MAX(SINFO.mana_max - (SINFO.mana_change * + (GET_SKILL(ch, SINFO.base_skill) - SINFO.min_level)), SINFO.mana_min); return mana; *************** *** 777,783 **** return; } if (!npc_casting) { ! if (GET_LEVEL(ch) < SINFO.min_level[(int) GET_CLASS(ch)]) { send_to_char("You do not know that spell!\r\n", ch); return; } --- 788,794 ---- return; } if (!npc_casting) { ! if (GET_SKILL(ch, SINFO.base_skill) < SINFO.min_level) { send_to_char("You do not know that spell!\r\n", ch); return; } *************** *** 901,907 **** return; } ! if (class < 0 || class >= NUM_CLASSES) { sprintf(buf, "SYSERR: assigning '%s' to illegal class %d", skill_name(spell), class); log(buf); --- 912,918 ---- return; } ! if (class < SKILL_MAGE || class >= (SKILL_MAGE + NUM_CLASSES)) { sprintf(buf, "SYSERR: assigning '%s' to illegal class %d", skill_name(spell), class); log(buf); *************** *** 915,922 **** bad = 1; } ! if (!bad) ! spell_info[spell].min_level[class] = level; } --- 926,935 ---- bad = 1; } ! if (!bad) { ! spell_info[spell].min_level = level; ! spell_info[spell].base_skill = class; ! } } *************** *** 927,933 **** int i; for (i = 0; i < NUM_CLASSES; i++) ! spell_info[spl].min_level[i] = LVL_IMMORT; spell_info[spl].mana_max = max_mana; spell_info[spl].mana_min = min_mana; spell_info[spl].mana_change = mana_change; --- 940,946 ---- int i; for (i = 0; i < NUM_CLASSES; i++) ! spell_info[spl].min_level = LVL_IMMORT; spell_info[spl].mana_max = max_mana; spell_info[spl].mana_min = min_mana; spell_info[spl].mana_change = mana_change; *************** *** 943,949 **** int i; for (i = 0; i < NUM_CLASSES; i++) ! spell_info[spl].min_level[i] = LVL_IMPL + 1; spell_info[spl].mana_max = 0; spell_info[spl].mana_min = 0; spell_info[spl].mana_change = 0; --- 956,962 ---- int i; for (i = 0; i < NUM_CLASSES; i++) ! spell_info[spl].min_level = LVL_IMPL + 1; spell_info[spl].mana_max = 0; spell_info[spl].mana_min = 0; spell_info[spl].mana_change = 0; *************** *** 1205,1214 **** skillo(SKILL_SNEAK); skillo(SKILL_STEAL); skillo(SKILL_TRACK); #ifdef RANGED_WEAPONS skillo(SKILL_SHOOT); skillo(SKILL_THROW); #endif - skillo(SKILL_SECOND_ATTACK); } --- 1218,1322 ---- skillo(SKILL_SNEAK); skillo(SKILL_STEAL); skillo(SKILL_TRACK); + skillo(SKILL_SECOND_ATTACK); + skillo(SKILL_THIRD_ATTACK); #ifdef RANGED_WEAPONS skillo(SKILL_SHOOT); skillo(SKILL_THROW); #endif } + + + /* + * SPELLS AND SKILLS. This area defines which spells are assigned to + * which classes, and the minimum level the character must be to use + * the spell or skill. + */ + void init_spell_levels(void) + { + /* MAGES */ + spell_level(SPELL_MAGIC_MISSILE, SKILL_MAGE, 1); + spell_level(SPELL_CREATE_LIGHT, SKILL_MAGE, 1); + spell_level(SPELL_DETECT_INVIS, SKILL_MAGE, 2); + spell_level(SPELL_DETECT_MAGIC, SKILL_MAGE, 2); + spell_level(SPELL_CHILL_TOUCH, SKILL_MAGE, 3); + spell_level(SPELL_INFRAVISION, SKILL_MAGE, 3); + spell_level(SPELL_INVISIBLE, SKILL_MAGE, 4); + spell_level(SPELL_ARMOR, SKILL_MAGE, 4); + spell_level(SPELL_BURNING_HANDS, SKILL_MAGE, 5); + spell_level(SPELL_LOCATE_OBJECT, SKILL_MAGE, 6); + spell_level(SPELL_STRENGTH, SKILL_MAGE, 6); + spell_level(SPELL_FAERIE_FIRE, SKILL_MAGE, 7); + spell_level(SPELL_SHOCKING_GRASP, SKILL_MAGE, 7); + spell_level(SPELL_SLEEP, SKILL_MAGE, 8); + spell_level(SPELL_LIGHTNING_BOLT, SKILL_MAGE, 9); + spell_level(SPELL_BLINDNESS, SKILL_MAGE, 9); + spell_level(SPELL_DETECT_POISON, SKILL_MAGE, 10); + spell_level(SPELL_LEVITATE, SKILL_MAGE, 10); + spell_level(SPELL_COLOR_SPRAY, SKILL_MAGE, 11); + spell_level(SPELL_ENERGY_DRAIN, SKILL_MAGE, 12); + spell_level(SPELL_CURSE, SKILL_MAGE, 13); + spell_level(SPELL_FIREBALL, SKILL_MAGE, 14); + spell_level(SPELL_STONESKIN, SKILL_MAGE, 15); + spell_level(SPELL_CHARM, SKILL_MAGE, 16); + spell_level(SPELL_HASTE, SKILL_MAGE, 17); + spell_level(SPELL_ICE_STORM, SKILL_MAGE, 19); + spell_level(SPELL_FIRE_SHIELD, SKILL_MAGE, 22); + spell_level(SPELL_INFERNO, SKILL_MAGE, 24); + spell_level(SPELL_ENCHANT_WEAPON, SKILL_MAGE, 26); + + + /* CLERICS */ + spell_level(SPELL_CURE_LIGHT, SKILL_CLERIC, 1); + spell_level(SPELL_REFRESH, SKILL_CLERIC, 1); + spell_level(SPELL_ARMOR, SKILL_CLERIC, 1); + spell_level(SPELL_CREATE_FOOD, SKILL_CLERIC, 2); + spell_level(SPELL_CREATE_WATER, SKILL_CLERIC, 2); + spell_level(SPELL_CAUSE_LIGHT, SKILL_CLERIC, 3); + spell_level(SPELL_DETECT_POISON, SKILL_CLERIC, 3); + spell_level(SPELL_DETECT_ALIGN, SKILL_CLERIC, 4); + spell_level(SPELL_CURE_BLIND, SKILL_CLERIC, 4); + spell_level(SPELL_CREATE_LIGHT, SKILL_CLERIC, 4); + spell_level(SPELL_BLESS, SKILL_CLERIC, 5); + spell_level(SPELL_DETECT_INVIS, SKILL_CLERIC, 6); + spell_level(SPELL_BLINDNESS, SKILL_CLERIC, 6); + spell_level(SPELL_INFRAVISION, SKILL_CLERIC, 7); + spell_level(SPELL_PROT_FROM_EVIL, SKILL_CLERIC, 8); + spell_level(SPELL_GROUP_ARMOR, SKILL_CLERIC, 9); + spell_level(SPELL_CURE_CRITIC, SKILL_CLERIC, 9); + spell_level(SPELL_SUMMON, SKILL_CLERIC, 10); + spell_level(SPELL_REMOVE_POISON, SKILL_CLERIC, 10); + spell_level(SPELL_CAUSE_CRITIC, SKILL_CLERIC, 11); + spell_level(SPELL_WORD_OF_RECALL, SKILL_CLERIC, 12); + spell_level(SPELL_EARTHQUAKE, SKILL_CLERIC, 12); + spell_level(SPELL_DISPEL_EVIL, SKILL_CLERIC, 14); + spell_level(SPELL_DISPEL_GOOD, SKILL_CLERIC, 14); + spell_level(SPELL_SANCTUARY, SKILL_CLERIC, 15); + spell_level(SPELL_CALL_LIGHTNING, SKILL_CLERIC, 15); + spell_level(SPELL_HEAL, SKILL_CLERIC, 16); + spell_level(SPELL_CONTROL_WEATHER, SKILL_CLERIC, 17); + spell_level(SPELL_HARM, SKILL_CLERIC, 19); + spell_level(SPELL_GROUP_HEAL, SKILL_CLERIC, 22); + spell_level(SPELL_REMOVE_CURSE, SKILL_CLERIC, 24); + spell_level(SPELL_BLADE_BARRIER, SKILL_CLERIC, 26); + spell_level(SPELL_HOLY_WORD, SKILL_CLERIC, 28); + + + /* THIEVES */ + spell_level(SKILL_SNEAK, SKILL_THIEF, 1); + spell_level(SKILL_PICK_LOCK, SKILL_THIEF, 2); + spell_level(SKILL_BACKSTAB, SKILL_THIEF, 3); + spell_level(SKILL_STEAL, SKILL_THIEF, 4); + spell_level(SKILL_HIDE, SKILL_THIEF, 5); + spell_level(SKILL_TRACK, SKILL_THIEF, 6); + + + /* WARRIORS */ + spell_level(SKILL_KICK, SKILL_WARRIOR, 1); + spell_level(SKILL_RESCUE, SKILL_WARRIOR, 3); + spell_level(SKILL_TRACK, SKILL_WARRIOR, 9); + spell_level(SKILL_BASH, SKILL_WARRIOR, 12); + spell_level(SKILL_SECOND_ATTACK, SKILL_WARRIOR, 15); + } + diff -bcN -C 3 --exclude=*.o --exclude=*.rej --exclude=*.orig --exclude=*~ tlo/ spells.h tlo_multiclass/spells.h *** tlo/spells.h Sat Mar 29 20:16:43 1997 --- tlo_multiclass/spells.h Sat Mar 29 20:10:13 1997 *************** *** 8,13 **** --- 8,16 ---- * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ + #define NUM_DISCIPLINES 4 + #define BASE_DISCIPLINE 481 + #define DEFAULT_STAFF_LVL 12 #define DEFAULT_WAND_LVL 12 *************** *** 123,132 **** #endif /* Daniel Houghton's replacements for classes */ ! #define SKILL_MAGE 480 ! #define SKILL_CLERIC 481 ! #define SKILL_THIEF 482 ! #define SKILL_WARRIOR 483 /* WEAPON ATTACK TYPES */ --- 126,135 ---- #endif /* Daniel Houghton's replacements for classes */ ! #define SKILL_MAGE 481 ! #define SKILL_CLERIC 482 ! #define SKILL_THIEF 483 ! #define SKILL_WARRIOR 484 /* WEAPON ATTACK TYPES */ *************** *** 200,207 **** int mana_min; /* Min amount of mana used by a spell (highest lev) */ int mana_max; /* Max amount of mana used by a spell (lowest lev) */ int mana_change; /* Change in mana used by spell from lev to lev */ ! ! int min_level[NUM_CLASSES]; int routines; byte violent; int targets; /* See below for use with TAR_XXX */ --- 203,211 ---- int mana_min; /* Min amount of mana used by a spell (highest lev) */ int mana_max; /* Max amount of mana used by a spell (lowest lev) */ int mana_change; /* Change in mana used by spell from lev to lev */ ! int base_skill; ! int min_level; ! /* int min_level[NUM_CLASSES]; */ int routines; byte violent; int targets; /* See below for use with TAR_XXX */ Common subdirectories: tlo/util and tlo_multiclass/util