From: Demond LaZaro <demond@tolkien.realms.org>
Subject: Multi-Classing/Level Code

This is my multi-classing/multi-level code.  If there are ay problems
adding this code to bpl11 let me know.  E-mail me at demond@bevy.com

here it is:
Structs.h:
~~~~~~~~~
Find:
byte class;

Add:
byte dual_class;
byte tri_class;

Find:
byte level;

Add:
byte dual_level;
byte tri_level;

Find one of the following:
#define CON_DELCNF2      16             /* Delete confirmation 2        */
#define CON_OEDIT        17             /*. OLC mode - object edit     .*/
#define CON_REDIT        18             /*. OLC mode - room edit       .*/
#define CON_ZEDIT        19             /*. OLC mode - zone info edit  .*/
#define CON_MEDIT        20             /*. OLC mode - mobile edit     .*/
#define CON_SEDIT        21             /*. OLC mode - shop edit       .*/
#define CON_QRACE        22

Add:
#define CON_QDUAL        **
#define CON_QTRI         **
/*  ** is = next number */

Utils.h:
~~~~~~~~
Find:
#define CLASS_ABBR....

Add:
#define DUAL_ABBR(ch) (IS_NPC(ch) ? "--" :
dual_abbrevs[(int)GET_DUAL(ch)])
#define TRI_ABBR(ch) (IS_NPC(ch) ? "--" : tri_abbrevs[(int)GET_TRI(ch)])

Class.c:
~~~~~~~~
Fi;nd:
const char *class_abbrevs[] = {
...
}

Add:
const char *dual_abbrevs[] = {
  "Mu",
  "Cl",
  "Th",
  "Wa",
  "\n"
};
const char *tri_abbrevs[] = {
  "Mu",
  "Cl",
  "Th",
  "Wa",
  "\n"
};

Magic.c
~~~~~~~
Find: /* in mag_damage */
  is_mage = (GET_CLASS(ch) == CLASS_MAGIC_USER);
  is_cleric = (GET_CLASS(ch) == CLASS_CLERIC);

Change it to:
  is_mage = (GET_CLASS(ch) == CLASS_MAGIC_USER ||
        GET_DUAL(ch) == CLASS_MAGIC_USER || GET_TRI(ch) == CLASS_MAGIC_USER);
  is_cleric = (GET_CLASS(ch) == CLASS_CLERIC ||
        GET_DUAL(ch) == CLASS_CLERIC || GET_TRI(ch) == CLASS_CLERIC);

Interpreter.c
~~~~~~~~~~~~~
Find: /* in nanny */
  case CON_QCLASS:
    load_result = parse_class(*arg);
    if (load_result == CLASS_UNDEFINED) {
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    } else
      GET_CLASS(d->character) = load_result;

Change it to:
  case CON_QCLASS:
    load_result = parse_class(*arg);
    if (load_result == CLASS_UNDEFINED) {
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    } else
      GET_CLASS(d->character) = load_result;

      SEND_TO_Q(class_menu, d);
      SEND_TO_Q("\r\nSecond Class: ", d);
      STATE(d) = CON_QDUAL;
      break;
  case CON_QDUAL:
    load_result = parse_class(*arg);
    if (load_result == CLASS_UNDEFINED 
	|| load_result == GET_CLASS(d->character));
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    } else
      GET_DUAL(d->character) = load_result;

      SEND_TO_Q(class_menu, d);
      SEND_TO_Q("\r\nThird Class: ", d);
      STATE(d) = CON_QTRI;
      break;

  case CON_QTRI:
    load_result = parse_class(*arg);
    if (load_result == CLASS_UNDEFINED 
	|| load_result == GET_CLASS(d->character));
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    } else
      GET_TRI(d->character) = load_result;

    if (GET_PFILEPOS(d->character) < 0)
      GET_PFILEPOS(d->character) = create_entry(GET_NAME(d->character));
    init_char(d->character);
    save_char(d->character, NOWHERE);
    save_player_index();
    SEND_TO_Q(motd, d);
    SEND_TO_Q("\r\n\n*** PRESS RETURN: ", d);
    STATE(d) = CON_RMOTD;

    sprintf(buf, "%s [%s] new player.", GET_NAME(d->character), d->host);
    mudlog(buf, NRM, LVL_IMMORT, TRUE);
    break;

Constants.c
~~~~~~~~~~~
Find one or all of the following:
  "Self-Delete 2",
  "Object edit",
  "Room edit",
  "Zone edit",
  "Mobile edit",
  "Shop edit",
  "Select race",

Add the following before "\n":
  "Select dual",
  "Select tri ",

And in Spec_procs.c
~~~~~~~~~~~~~~~~~~~~
/************************************************************************
 *      Special procedure for gaining levels on a non-autolevel         *
 * Mud.  This was 100% coded by Demond LaZaro please add him in the     *
 * credits and drop him an e-mail at demond@realms.org.                 *
 ***********************************************************************/

SPECIAL(level)
{

  int add_hp = 0, add_mana = 0, add_move = 0, i;
  extern char *class_abbrevs[];
  extern char *dual_abbrevs[];
  extern char *tri_abbrevs[];
  extern struct wis_app_type wis_app[];
  extern struct con_app_type con_app[];
  extern struct title_type titles[NUM_CLASSES][LVL_IMPL + 1];

  add_hp = con_app[GET_CON(ch)].hitp;

  skip_spaces(&argument);

  if (IS_NPC(ch) || !CMD_IS("gain")) {
    return 0; }

  switch(*argument) {
    case '1':
      if (GET_EXP(ch) >= titles[(int) GET_CLASS(ch)][GET_LEVEL(ch) + 1].exp) {
        GET_EXP(ch) -= titles[(int) GET_CLASS(ch)][GET_LEVEL(ch) + 1].exp;
        GET_LEVEL(ch) += 1;
        send_to_char("You have Leveled!!", ch);
        sprintf(buf, "%s advanced to level %d in %s", GET_NAME(ch),
                GET_LEVEL(ch), CLASS_ABBR(ch));
        mudlog(buf, BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
        switch (GET_CLASS(ch)) {

          case CLASS_MAGIC_USER:
            add_hp += number(3, 8);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_CLERIC:
            add_hp += number(5, 10);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_THIEF:
            add_hp += number(7, 13);
            add_mana = 0;
            add_move = number(1, 3);
            break;

          case CLASS_WARRIOR:
            add_hp += number(10, 15);
            add_mana = 1;
            add_move = number(1, 3);
            break;
        }
        break;
      } else {
        send_to_char("You Don't have enouph experience to level!", ch);
        break; }
      break;
    case '2':
      if (GET_EXP(ch) >= titles[(int) GET_DUAL(ch)][GET_DUAL_LEVEL(ch) + 1].exp) {
        GET_EXP(ch) -= titles[(int) GET_DUAL(ch)][GET_DUAL_LEVEL(ch) + 1].exp;
        GET_DUAL_LEVEL(ch) += 1;
        sprintf(buf, "%s advanced to level %d in %s", GET_NAME(ch),
                GET_DUAL_LEVEL(ch), DUAL_ABBR(ch));
        mudlog(buf, BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
        switch (GET_DUAL(ch)) {

          case CLASS_MAGIC_USER:
            add_hp += number(3, 8);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_CLERIC:
            add_hp += number(5, 10);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_THIEF:
            add_hp += number(7, 13);
            add_mana = 0;
            add_move = number(1, 3);
            break;

          case CLASS_WARRIOR:
            add_hp += number(10, 15);
            add_mana = 1;
            add_move = number(1, 3);
            break;
        }
        break;
      } else {
        send_to_char("You Don't have enouph experience to level!", ch);
        break; }
      break;
    case '3':
      if (GET_EXP(ch) >= titles[(int) GET_TRI(ch)][GET_TRI_LEVEL(ch) + 1].exp) {
        GET_EXP(ch) -= titles[(int) GET_TRI(ch)][GET_TRI_LEVEL(ch) + 1].exp;
        GET_TRI_LEVEL(ch) += 1;
        sprintf(buf, "%s advanced to level %d in %s", GET_NAME(ch),
                GET_TRI_LEVEL(ch), TRI_ABBR(ch));
        mudlog(buf, BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE);
        switch (GET_TRI(ch)) {

          case CLASS_MAGIC_USER:
            add_hp += number(3, 8);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_CLERIC:
            add_hp += number(5, 10);
            add_mana = number(GET_LEVEL(ch), (int) (1.5 * GET_LEVEL(ch)));
            add_mana = MIN(add_mana, 10);
            add_move = number(0, 2);
            break;

          case CLASS_THIEF:
            add_hp += number(7, 13);
            add_mana = 0;
            add_move = number(1, 3);
            break;

          case CLASS_WARRIOR:
            add_hp += number(10, 15);
            add_mana = 1;
            add_move = number(1, 3);
            break;
        }
        break;
      } else {
        send_to_char("You Don't have enouph experience to level!", ch);
        break; }
      break;
    default:
      send_to_char("Thats not a valid class number!\r\n", ch);
      send_to_char("Try 1 for first class, 2 for second, and 3 for third.", ch);
      break;
  }
  ch->points.max_hit += MAX(1, add_hp);
  ch->points.max_move += MAX(1, add_move);

  if (GET_LEVEL(ch) > 1)
    ch->points.max_mana += add_mana;

  if (GET_CLASS(ch) == CLASS_MAGIC_USER || GET_CLASS(ch) == CLASS_CLERIC)
    GET_PRACTICES(ch) += MAX(2, wis_app[GET_WIS(ch)].bonus);
  else
    GET_PRACTICES(ch) += MIN(2, MAX(1, wis_app[GET_WIS(ch)].bonus));

  if (GET_LEVEL(ch) >= LVL_IMMORT) {
    for (i = 0; i < 3; i++)
      GET_COND(ch, i) = (char) -1;
    SET_BIT(PRF_FLAGS(ch), PRF_HOLYLIGHT);
  }

  save_char(ch, NOWHERE);

  return 1;
}
