/****************************************************************************
 * Multiclass Code copyright 1999-2001                                      *
 * Markanth : markanth@spaceservices.net                                    *
 * Devil's Lament : spaceservices.net port 3778                             *
 * Web Page : http://spaceservices.net/~markanth/                           *
 *                                                                          *
 * A nice and simple remort code if you like multiclassing.                 *
 * This code require you have a working knowledge of how rom skills work.   *
 *                                                                          *
 * All I ask in return is that you give me credit on your mud somewhere     *
 * or email me if you use it.                                               *
 ***************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"

/* Use for things like:
 *  ch->level >= skill_table[sn].skill_level[ch->class]
 */
bool can_use_skpell(CHAR_DATA * ch, int sn)
{
    if (IS_NPC(ch))
	return TRUE;

    if (is_race_skill(ch, sn))
	return TRUE;

    if (ch->level >= skill_level(ch, sn))
	return TRUE;

	return FALSE;
}

/* Used for things like:
 *  class_table[ch->class].fMana
 */
bool has_spells(CHAR_DATA * ch)
{
    int iClass;

    if (IS_NPC(ch))
	return FALSE;
    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	if (class_table[iClass].fMana)
	    return TRUE;
    return FALSE;
}

/* Used for things like
 *   ch->class == 2
 */
bool is_class(CHAR_DATA * ch, int class)
{
    int iClass;

    if (IS_NPC(ch))
	return FALSE;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (ch->class[iClass] == class)
	    return TRUE;
    }

    return FALSE;
}

/* Used for things like:
 *   ch->class == victim->class
 */
bool is_same_class(CHAR_DATA * ch, CHAR_DATA * victim)
{
    int iClass;

    if (IS_NPC(ch) || IS_NPC(victim))
	return FALSE;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (ch->class[iClass] == victim->class[iClass])
	    return TRUE;
    }

    return FALSE;
}

/* Returns a users prime class (first class) */
int prime_class(CHAR_DATA * ch)
{
    return ch->class[0];
}

/* Returns the number of classes a user has */
int number_classes(CHAR_DATA * ch)
{
    int iClass;

    if (IS_NPC(ch))
	return 0;
    for (iClass = 0; ch->class[iClass] != -1; iClass++);
    return iClass;
}

/* Outputs class names in long format */
char *class_long(CHAR_DATA * ch)
{
    static char buf[512];
    int iClass;

    buf[0] = '\0';
    if (IS_NPC(ch))
	return "Mobile";
    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	strcat(buf, "/");
	strcat(buf, class_table[ch->class[iClass]].name);
    }
    return buf + 1;
}

/* Outputs class names in a 3 letter who format
 *  Ex. W+3  mean Warrior plus 3 remorts (4 classes in total)
 */
char *class_who(CHAR_DATA * ch)
{
    static char buf[512];

    buf[0] = '\0';
    if (IS_NPC(ch))
	return "Mob";

    if (number_classes(ch) > 1)
	sprintf(buf, "%c+%d", class_table[ch->class[0]].who_name[0], number_classes(ch) - 1);
    else
	sprintf(buf, class_table[ch->class[0]].who_name);
    return buf;
}

/* Outputs class names in short format */
char *class_short(CHAR_DATA * ch)
{
    static char buf[512];
    int iClass;

    buf[0] = '\0';
    if (IS_NPC(ch))
	return "Mob";
    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	strcat(buf, "/");
	strcat(buf, class_table[ch->class[iClass]].who_name);
    }
    return buf + 1;
}

/* Sends number of classes to a string, Used for saving. */
char *class_numbers(CHAR_DATA * ch, bool pSave)
{
    static char buf[512];
    char buf2[10];
    int iClass;

    buf[0] = '\0';

    if (IS_NPC(ch))
	return "0";

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	strcat(buf, " ");
	sprintf(buf2, "%d", ch->class[iClass]);
	strcat(buf, buf2);
    }
    if (pSave)
	strcat(buf, " -1");
    return buf + 1;
}

/* Used for things like:
 *   level = skill_table[sn].skill_level[ch->class]
 * Finds the lowest skill level for all classes.
 */
int skill_level(CHAR_DATA * ch, int sn)
{
    int iClass;
    int tempskill = 999;

    if (is_race_skill(ch, sn))
	return 1;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (!can_gain_skill(ch, sn))
	    continue;
	tempskill = UMIN(tempskill, skill_table[sn].skill_level[ch->class[iClass]]);
    }
    return tempskill;
}

/* Used for things like:
 *   train = skill_table[sn].rating[ch->class]
 * Finds the lowest skill rating for all classes.
 */
int skill_rating(CHAR_DATA * ch, int sn)
{
    int iClass;
    int temprate = 999;

    if (is_race_skill(ch, sn))
	return 2;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (skill_table[sn].rating[ch->class[iClass]] < 1)
	    continue;
	temprate = UMIN(temprate, skill_table[sn].rating[ch->class[iClass]]);
    }
    return temprate;
}

/* Used for things like:
 *   train = group_table[sn].rating[ch->class]
 * Finds the lowest group rating for all classes.
 */

int group_rating(CHAR_DATA * ch, int gn)
{
    int iClass;
    int temprate = 999;

    if (is_race_skill(ch, gn))
	return 2;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (group_table[gn].rating[ch->class[iClass]] < 1)
	    continue;
	temprate = UMIN(temprate, group_table[gn].rating[ch->class[iClass]]);
    }
    return temprate;
}

/* Used to find the max amount of hp gain for a class in advance_level() */
int get_hp_gain(CHAR_DATA * ch)
{
    int iClass = 0;
    int gain = 0;
    int count = 0;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	gain = UMAX(gain, number_range(class_table[ch->class[iClass]].hp_min, class_table[ch->class[iClass]].hp_max));
	count++;
    }
    return number_range(gain, gain + count);
}

/* Used to find if a stat is a prime of a users classes */
bool is_prime_stat(CHAR_DATA * ch, int stat)
{
    int iClass = 0;

    if (IS_NPC(ch))
	return TRUE;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if (class_table[ch->class[iClass]].attr_prime == stat)
	    return TRUE;
    }
    return FALSE;
}

/* Adds all class default groups while keeping the charge at 40 */
void add_default_groups(CHAR_DATA * ch)
{
    int iClass = 0;

    if (IS_NPC(ch))
	return;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	if(class_table[ch->class[iClass]].default_group != NULL)
	group_add(ch, class_table[ch->class[iClass]].default_group, FALSE);

    ch->pcdata->points += 40;
}

/* Adds all class base groups */
void add_base_groups(CHAR_DATA * ch)
{
    int iClass = 0;

    if (IS_NPC(ch))
	return;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	if(class_table[ch->class[iClass]].base_group != NULL)
	group_add(ch, class_table[ch->class[iClass]].base_group, FALSE);
}

/* Returns TRUE if a group number is a class base group */
bool check_base_group(CHAR_DATA *ch, int gn)
{
    int iClass = 0;

    if(IS_NPC(ch))
	return FALSE;

	if(gn < 0 || gn > MAX_GROUP)
		return FALSE;

    for(iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if(class_table[ch->class[iClass]].base_group == NULL)
	continue;

	if(group_lookup(class_table[ch->class[iClass]].base_group) == gn)
		return TRUE;
    }
    return FALSE;
}

/* returns TRUE if a skill number is a class base skill */
bool is_base_skill(CHAR_DATA *ch, int sn)
{
    int iClass = 0;
    int gn, x;

    if(IS_NPC(ch))
	return FALSE;

    if(sn < 0 || sn > MAX_SKILL)
	return FALSE;

    for(iClass = 0; ch->class[iClass] != -1; iClass++)
    {
	if(class_table[ch->class[iClass]].base_group == NULL)
	continue;

	if((gn = group_lookup(class_table[ch->class[iClass]].base_group)) != -1)
	{
		for(x = 0; x < MAX_IN_GROUP; x++)
		{
		if(group_table[gn].spells[x] == NULL)
		break;

		if(skill_lookup(group_table[gn].spells[x]) == sn)
		return TRUE;
		}
	}
    }
    return FALSE;
}

/* Gets lowest thac00 for all classes */
int get_thac00(CHAR_DATA * ch)
{
    int temp = 999,
     iClass = 0;

    if (IS_NPC(ch))
	return 0;
    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	temp = UMIN(temp, class_table[ch->class[iClass]].thac0_00);

    return temp;
}

/* gets lowest thac32 for all classes */
int get_thac32(CHAR_DATA * ch)
{
    int temp = 999,
     iClass = 0;

    if (IS_NPC(ch))
	return 0;
    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	temp = UMIN(temp, class_table[ch->class[iClass]].thac0_32);

    return temp;
}

/* Used for race exp multiplications */
long multiply_exp(CHAR_DATA * ch, long exp)
{
    int temp = 999,
     iClass = 0;

    if (IS_NPC(ch))
	return 0;

    for (iClass = 0; ch->class[iClass] != -1; iClass++)
	temp = UMIN(temp, pc_race_table[ch->race].class_mult[ch->class[iClass]]);

    exp *= temp;
    exp /= 100;

    return exp;
}

/* Simple bonus function for eq levels */
int lvl_bonus(CHAR_DATA * ch)
{
    return MAX_CLASS + number_classes(ch);
}

/* Returns TRUE if skill number is a race skill */
bool is_race_skill(CHAR_DATA * ch, int sn)
{
    int i;

    for (i = 0; i < 5; i++)
    {
	if (pc_race_table[ch->race].skills[i] == NULL)
	    continue;
	if (skill_lookup(pc_race_table[ch->race].skills[i]) == sn)
	    return TRUE;
	if (group_lookup(pc_race_table[ch->race].skills[i]) == sn)
	    return TRUE;
    }
    return FALSE;
}

>>>> MERC.H <<<<

1.  define:
#define MAX_MCLASS   (MAX_CLASS + 3)  // only seems to work this way

2.  in char_data structure change:
        int class;
        to:
        int class[MAX_MCLASS]
        add:
        bool confirm_remort;
		add:
		bool stay_race;

3.  define under PLR bits:

   #define PLR_REMORT  (Z)  /* whatevers free */
        
4.  in character macros define:
    
   #define IS_REMORT(ch)      (!IS_NPC(ch) && ( IS_SET((ch)->act, PLR_REMORT) \
                                || number_classes(ch) > 1))

5. change get_carry_weight(ch) to this:

#define get_carry_weight(ch)    ((ch)->carry_weight + number_classes(ch))

6. change GET_AC, GET_HITROLL and GET_DAMROLL to these:

#define GET_AC(ch,type)         ((ch)->armor[type]                            \
                        + ( IS_AWAKE(ch)                                      \
                        ? dex_app[get_curr_stat(ch,STAT_DEX)].defensive : 0 ) \
                        - number_classes(ch))
#define GET_HITROLL(ch) \
((ch)->hitroll+str_app[get_curr_stat(ch,STAT_STR)].tohit + number_classes(ch))
#define GET_DAMROLL(ch) \
    ((ch)->damroll+str_app[get_curr_stat(ch,STAT_STR)].todam + number_classes(ch))

6.  add these definitions:

/* multiclass.c */
bool can_use_skpell args ((CHAR_DATA * ch, int sn));
bool has_spells args ((CHAR_DATA * ch));
bool is_class args ((CHAR_DATA * ch, int class));
bool is_same_class args ((CHAR_DATA * ch, CHAR_DATA * victim));
int number_classes args ((CHAR_DATA * ch));
char *class_long args ((CHAR_DATA * ch));
char *class_who args ((CHAR_DATA * ch));
char *class_short args ((CHAR_DATA * ch));
char *class_numbers args ((CHAR_DATA * ch, bool pSave));
int skill_level args ((CHAR_DATA * ch, int sn));
int skill_rating args ((CHAR_DATA * ch, int sn));
int group_rating args ((CHAR_DATA * ch, int gn));
bool can_gain_skill args ((CHAR_DATA * ch, int sn));
bool can_gain_group args ((CHAR_DATA * ch, int gn));
int get_hp_gain args ((CHAR_DATA * ch));
int get_mana_gain args ((CHAR_DATA * ch));
bool is_prime_stat args ((CHAR_DATA * ch, int stat));
void add_default_groups args ((CHAR_DATA * ch));
void add_base_groups args ((CHAR_DATA * ch));
int get_stat_bonus args ((CHAR_DATA * ch, int stat));
int get_thac00 args ((CHAR_DATA * ch));
int get_thac32 args ((CHAR_DATA * ch));
int get_hp_max args ((CHAR_DATA * ch));
int lvl_bonus args ((CHAR_DATA * ch));
long multiply_exp args ((CHAR_DATA * ch, long exp));
bool is_race_skill args((CHAR_DATA *ch, int sn));

>>>> SAVE.C <<<<

1.  In fwrite_char change class to:

    fprintf( fp, "Cla  %s\n",	class_numbers(ch, TRUE)		);
	if (ch->pcdata->stay_race)
	    fprintf(fp, "StayRace\n");

2.  In fread_char change class to:

    if ( !str_cmp( word, "Cla" ) ||  !str_cmp( word, "Class" ))
    {
          int iClass=0;
          fMatch = TRUE;
          for ( iClass = 0; iClass < MAX_MCLASS; iClass++ )  //this will make old pfiles compatible
               ch->class[ iClass ] = -1;
          
          for ( iClass = 0; iClass < MAX_MCLASS; iClass++ )
          {
               ch->class[ iClass ] = fread_number( fp );
               // you''ll have to do some versioning here.
               if ( ch->class[ iClass ] == -1 || ch->version < /*old version*/)
                   break;
          }
     }

	Add under case 'S':

		if (!str_cmp(word, "StayRace"))
		{
		    ch->pcdata->stay_race = TRUE;
		    fMatch = TRUE;
		    break;
		}

>>>> ACT_WIZ.C <<<<

1. in do_mset() replace 'class' subfunction with this

/* Set up for 7 classes only */
    if (!str_cmp(arg2, "class"))
    {
	char arg4[MIL];
	char buf[MSL];
	int slot,
	 value,
	 iClass;

	argument = one_argument(argument, arg3);
	strcpy(arg4, argument);
	slot = (is_number(arg3)) ? atoi(arg3) : -1;
	value = (is_number(arg4)) ? atoi(arg4) : -1;
	if (value < -1)
	    value = -1;
	if (slot < 1 || slot > 7)
	{
	    chsend("Invalid slot.  Valid slot: 1-7.\n\r", ch);
	    return;
	}
	for (iClass = 0; iClass < MAX_CLASS; iClass++)
	{
	    if (!str_prefix(arg4, class_table[iClass].name) || !str_cmp(arg4, class_table[iClass].who_name))
	    {
		value = iClass;
		break;
	    }
	}
	if (slot == 1 && value == -1)
	{
	    chsend("Cannot turn off 1st Class.\n\r", ch);
	    return;
	}
	if ((slot == 2 && value == -1 && victim->class[2] != -1) || (slot == 3 && value == -1 && victim->class[3] != -1) || (slot == 4 && value == -1 && victim->class[4] != -1) || (slot == 5 && value == -1 && victim->class[5] != -1) || (slot == 6 && value == -1 && victim->class[6] != -1) || (slot == 7 && value == -1 && victim->class[7] != -1))
	{
	    chsend("You must turn off next to last class first.\n\r", ch);
	    return;
	}
	if ((slot == 3 && victim->class[1] == -1) || (slot == 4 && victim->class[2] == -1) || (slot == 5 && victim->class[3] == -1) || (slot == 6 && victim->class[4] == -1) || (slot == 7 && victim->class[5] == -1))
	{
	    act("Cannot set that Class for $N.  Player does not have a Previous Class.", ch, NULL, victim, TO_CHAR);
	    return;
	}
	if (value < -1 || value > MAX_CLASS)
	{
	    sprintf(buf, "Class range is 0 to %d.\n\r", MAX_CLASS - 1);
	    chsend(buf, ch);
	    for (iClass = 0; iClass < MAX_CLASS; iClass++)
	    {
		sprintf(buf, " %2d = %s\n\r", iClass, class_table[iClass].name);
		chsend(buf, ch);
	    }
	    chsend(" -1 = Turns off class.\n\r", ch);
	    return;
	}
	if (value != -1 && is_class(victim, value))
	{
	    sprintf(buf, "$N is already %s %s.", (victim->class[1] != -1) ? "part" : "a", class_table[value].name);
	    act(buf, ch, NULL, victim, TO_CHAR);
	    return;
	}
	victim->class[slot - 1] = value;
	if (slot == 8)
	    victim->class[slot] = -1;
	chsend("Ok.\n\r", ch);
	return;
    }

>>>> ACT_ENTER.C / WHEREVER <<<<

1. Add these functions or similar:

void do_remor(CHAR_DATA * ch, char *argument)
{
    send_to_char("If you want to REMORT, you must spell it out.\n\r", ch);
    return;
}

void do_remort(CHAR_DATA * ch, char *argument)
{
    DESCRIPTOR_DATA *d;
    CHAR_DATA *mob;
    AFFECT_DATA *af,
    *af_next;
	char buf[MIL];
    int x,
     sn;

    if (IS_NPC(ch) || (d = ch->desc) == NULL)
	return;

    /*
     * check for priest or special mob
     */
    for (mob = ch->in_room->people; mob; mob = mob->next_in_room)
    {
	if (IS_NPC(mob) && IS_SET(mob->act, ACT_IS_PRIEST))
	    break;
    }

    if (mob == NULL)
    {
	send_to_char("You can't do that here.\n\r", ch);
	return;
    }

/* Legend = HERO + 1, gained not by exp but gold and quest points.
   Optionally you can add a check for gold/questpoints here. */
    if (ch->level != LEVEL_LEGEND)
    {
	send_to_char("You must be a LEGEND to remort.\n\r", ch);
	return;
    }

    if (number_classes(ch) == MAX_CLASS) // have all classes already
    {
	send_to_char("Remort into What??\n\r", ch);
	return;
    }

    if (IS_QUESTOR(ch)) // not if on quest. see dlm_quest.c
    {
	send_to_char("Don't you want to finish your quest first?\n\r", ch);
	return;
    }

    for (x = 0; x < MAX_WEAR; x++)
    {
	if (get_eq_char(ch, x) != NULL)
	{
	    send_to_char("Remove all of your eq first. (heal uncurse for cursed items)\n\r", ch);
	    return;
	}
    }

    if (ch->pcdata->confirm_remort)
    {
	if (!IS_NULLSTR(argument))
	{
	    send_to_char("Remort status removed.\n\r", ch);
	    ch->pcdata->confirm_remort = FALSE;
	    return;
	} else
	{
	    sprintf(buf, "{xYou have chosen to {Rremort{x.  You will now be dropped in at the %s\n\r", !ch->pcdata->stay_race ? "{RRACE{x" : "{BCLASS{x");
		csend_to_char(buf, ch);
	    send_to_char("selection section of character creation, and will be allowed recreate\n\r", ch);
	    send_to_char("your character with an additional {Cclass{x and {Gbonuses{x.\n\r\n\r", ch);
	    send_to_char("In the unlikely event that you are disconnected or the MUD\n\r", ch);
	    send_to_char("crashes while you are creating your character, log back on and write a\n\r", ch);
	    send_to_char("note to 'immortal' who will retrieve your backup.\n\r", ch);
	    wiznet("$N has remorted.", ch, NULL, 0, 0, 0);
	    backup_char_obj(ch); // backup the pfile first
	    for (af = ch->affected; af != NULL; af = af_next)
	    {
		af_next = af->next;
		affect_remove(ch, af);
	    }

	    SET_BIT(ch->act, PLR_REMORT);
            /* get rid of unwanted messages while in nanny() */
	    SET_BIT(ch->comm, COMM_QUIET);
	    char_from_room(ch);
	    char_to_room(ch, get_room_index(ROOM_VNUM_LIMBO));
	    ch->level = 1;
	    ch->pcdata->points = 0;
	    ch->exp = 0;
	    if (ch->pet != NULL)
	    {
		nuke_pets(ch);
		ch->pet = NULL;
	    }
            /* Race skills are lost.
               100% skills are kept at 100%.
               All other skills are reset back to 1%. */
	    for (sn = 0; sn < MAX_SKILL; sn++)
	    {
		if (ch->pcdata->learned[sn] > 0)
		{
		    if (is_race_skill(ch, sn))
			ch->pcdata->learned[sn] = 0;
		    else if (ch->pcdata->learned[sn] == 100)
			continue;
		    else
			ch->pcdata->learned[sn] = 1;
		}
	    }
            /* send char to race selection, customize this as you see fit */
	    send_to_char("\n\r{RNow beginning the remorting process.{x\n\r\n\r", ch);

	    if (!ch->pcdata->stay_race)
	    {
            send_to_char("The following races are available:\n\r  ",ch);
            for ( race = 1; race_table[race].name != NULL; race++ )
            {
                    if (!race_table[race].pc_race)
                        break;
                    send_to_char(race_table[race].name,ch);
                    send_to_char(" ",ch);
            }
            send_to_char("\n\rWhat is your race (help for more information)?",ch);
	    d->connected = CON_GET_NEW_RACE;
	    } else
	    {
			sprintf( buf, "Select class number %d [ ", (number_classes(ch) + 1) );
			for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
			{
				if (is_class(ch, iClass))
				continue;
				strcat( buf, class_table[iClass].name );
			}
			strcat( buf, "]: " );
			send_to_char( buf, ch );
			  d->connected = CON_GET_NEW_CLASS;
		}
	    return;
	}
    }

    if (!IS_NULLSTR(argument))
    {
	send_to_char("Just type remort.  No argument.\n\r", ch);
	return;
    }
    send_to_char("{RTyping {Gremort{R with an argument will undo remort status.{x\n\r", ch);
    send_to_char("{RRemorting is {Wnot reversable{R, make sure you read help REMORT\n\r", ch);
    send_to_char("{Rand have an idea of what {Cclass{R you want to remort into.{x\n\r", ch);
    send_to_char("{RType {Gremort{R again to confirm this command.{x\n\r", ch);
    ch->pcdata->confirm_remort = TRUE;
	if (!ch->pcdata->stay_race)
		chsend("{RWARNING: {WIF YOU CHOOSE A RACE DIFFERENT FROM YOUR RACE NOW YOU WILL BE THAT RACE FOREVER.{x\n\r\n\r", ch);
    wiznet("$N is contemplating remorting.", ch, NULL, 0, 0, get_trust(ch));
}

>>>> INTERP.C <<<<<<

1. add to command table:
        
  {"remor", do_remor, POS_DEAD, LG, LOG_ALWAYS, 0},
  {"remort", do_remort, POS_STANDING, LG, LOG_ALWAYS, 8},

>>>>> INTERP.H <<<<<<<

1. add:

DECLARE_DO_FUN (do_remor);
DECLARE_DO_FUN (do_remort);

>>>> COMM.C <<<<


1.  In CON_GET_NEW_RACE just before we set the race add:

	    if (IS_SET(ch->act, PLR_REMORT) && ch->race != race)
	    {
		sprintf(buf, "You are now a %s forever.\n\r", pc_race_table[ch->race].name);
		write_to_buffer(d, buf, 0);
		ch->pcdata->stay_race = TRUE;
	    }

1.  Just before we get to CON_GET_NEW_CLASS (GET_NEW_SEX in stock rom)

      ...

	strcpy( buf, "Select a class [ " );
	for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
	{
          /* Add/change next line */
	    if (IS_SET(ch->act, PLR_REMORT) && is_class(ch, iClass))
		continue;
	    strcat( buf, class_table[iClass].name );
	}
	strcat( buf, "]: " );
	write_to_buffer( d, buf, 0 );
	  d->connected = CON_GET_NEW_CLASS;
	}
      break;


2.  Change your CON_GET_NEW_CLASS to something like this:

    case CON_GET_NEW_CLASS:
	iClass = class_lookup(argument);

    /* Change next line */
	if ( iClass == -1 || (IS_SET(ch->act, PLR_REMORT) && is_class(ch, iClass)))
	{
	    write_to_buffer( d,
		"That's not a class.\n\rWhat IS your class? ", 0 );
	    return;
	}

        /* Set the class. */
	ch->class[number_classes(ch)] = iClass;  
	ch->class[number_classes(ch) + 1] = -1;
        /* Last class is ALWAYS -1 */

	sprintf( log_buf, "%s@%s new player.", ch->name, d->host );
	log_string( log_buf );
	wiznet("Newbie alert!  $N sighted.",ch,NULL,WIZ_NEWBIE,0,0);
        wiznet(log_buf,NULL,NULL,WIZ_SITES,0,get_trust(ch));

	write_to_buffer( d, "\n\r", 2 );
	write_to_buffer( d, "You may be good, neutral, or evil.\n\r",0);
	write_to_buffer( d, "Which alignment (G/N/E)? ",0);
	d->connected = CON_GET_ALIGNMENT;
	break;

4. in CON_GET_ALIGNMENT or where the recall skill is set

        change:
        ch->pcdata->learned[gsn_recall] = 50;
        to:
	    ch->pcdata->learned[gsn_recall] = UMAX(50, ch->pcdata->learned[gsn_recall]);

3. in CON_DEFAULT CHOICE change/add:

	case 'y':
	case 'Y':
	  ch->gen_data = new_gen_data ();
	  ch->gen_data->points_chosen = ch->pcdata->points;
	  do_function (ch, &do_help, "group header");
	  list_group_costs (ch);
          if(!IS_SET(ch->act, PLR_REMORT)) /* too spammy at tier 6 or 7 */
          {
              write_to_buffer (d,"{GYou already have the following skills:{x\n\r",0);
              do_function (ch, &do_skills, "");
          }
	  do_function (ch, &do_oldhelp, "menu choice");
	  d->connected = CON_GEN_GROUPS;
	  break;


4. At the end of CON_PICK_WEAPON change/add:

      ...

      ch->pcdata->learned[*weapon_table[weapon].gsn] = 
        UMAX(40, ch->pcdata->learned[*weapon_table[weapon].gsn]);

      write_to_buffer (d, "\n\r", 2);
      if (IS_SET (ch->act, PLR_REMORT))
      {
	  write_to_buffer (d, "\n\r", 2);
	  write_to_buffer (d, "{GSucessful Remort!{x\n\r", 0);
	  wiznet ("Sucessful Remort!  $N{g sighted.", ch, NULL, WIZ_MISC, 0, 0);
	  REMOVE_BIT (ch->comm, COMM_QUIET);
          ch->max_hit = 100 * number_classes (ch);
          ch->max_mana = 100 * number_classes (ch);
          ch->max_move = 100 * number_classes (ch);
          ch->hit = ch->max_hit;
          ch->mana = ch->max_move;
          ch->move = ch->max_mana;
          ch->pcdata->perm_hit = ch->max_hit;
          ch->pcdata->perm_mana = ch->max_mana;
          ch->pcdata->perm_move = ch->max_move;
          ch->wimpy = ch->max_hit / 5;
	  ch->train = 5 * number_classes (ch);
	  ch->practice = 7 * number_classes (ch);
          ch->exp = exp_per_level (ch, ch->pcdata->points);
          reset_char (ch);
          char_from_room(ch);
          char_to_room(ch, get_room_index(ROOM_VNUM_SCHOOL));
	  d->connected = CON_PLAYING;
	  act("The Priest brings you back to reality, and you feel quite different now...", ch, NULL, NULL, TO_CHAR);
	  do_function (ch, &do_outfit, "");
	  do_function (ch, &do_pack, "self");
	  save_char_obj (ch);
	  break;  /* Thats it for remorts */
      }
      do_function (ch, &do_oldhelp, "motd");
      d->connected = CON_READ_MOTD;
      break;

>>> FYI <<<<<

Here's how I handle exp with remorts.  This is just an example you could do something entirly different.

long exp_per_level (CHAR_DATA * ch, int points)
{
  int inc;
  long expl;

  if (IS_NPC (ch))
    return 1000;

  expl = 1000 * number_classes (ch);  /* base increments each remort */

  if (points >= 50)  /* 50 creation points before exp mods */
    {
      /*
       * processing 
       */
      inc = 500;
      points -= 50;

      while (points >= 10)
	{
	  expl += inc;
	  points -= 10;
	  if (points >= 10)
	    {
	      expl += inc;
	      inc *= 2;
	      points -= 10;
	    }
	}
      expl += points * inc / 10;
    }

  return multiply_exp (ch, expl);  /* handles race multipliers */

}


>>> LASTLY <<<<

    all references to foo->class  will have to be changed.
    basically all of them can be covered by the functions on multiclass.c
    look at the functions and see if there is anything familiar with what
    your replacing.

For example all references to:

        'class_table[ch->class].name' can become either
        class_who(ch), class_long(ch) or class_short(ch).

        skill_table[sn].rating[ch->class];
                becomes
        skill_rating(ch, sn);

        skill_table[sn].level[ch->class];
                becomes
        skill_level(ch, sn);

        group_table[gn].rating[ch->class];
                becomes
        group_rating(ch, gn);

Since remort chars will get extra skill/spells it I recomend that you
reorganize skills for each class, also there is lots of places you
can add bonuses for remort chars, in stat bonuses, eq level bonuses, pk ranges,
max_weight, max_carry, hunger/thirst using number_classes(char) to count # of remorts.

>>>>> END LASTLY <<<<<<<

>>>> HELP FILE <<<<<<<

-1 REMORT MULTICLASS~

When you reach LEGEND level on Devils Lament, you can continue to 
gain hp, mana, trains, pracs, moves. Alternatively, you can choose
to be 'reborn' into another class. What basically happens is that 
you add another class to your existing set of skills and spells and
restart at level 1. Because Devils Lament has 7 classes, a player can now
'go around' the whole game 7 times. But, this will get harder each 
time. If you choose to change races while remorting you will be that race
FOREVER, please be sure you want to change races.

There are many features to multiclass playing, but, the main ones 
of note are:

* Most of your existing skills/spells are reset to 1%, when you reach
  the correct level, you can practice them again. In the meantime, you
  gain and practice skills/spells in the new class. Race skills are lost
  at remort regardless of class, and 100% in a skill stays at 100%. 
* Gold, trivia points, quest points, gamestats ect. are kept.
* Quest items can be kept they are reset to your level.
* You can access the class channels for all the classes you have.
* Remorted chars will retain access to hero channel.
* Online time will remain untouched. 
* Different high power skills are available to the number of times
  you have remorted.
* Remort chars receive bonuses to level restrictions on eq each remort.
* Remorts retain hero+ status thus feel less hunger/thirst.
* Second time round, exp per level is 2k, third time 3k and so on.
  You also 'begin' with many extra training and practice sessions.
* You get the lowest level of a skill/spell if two or more of your 
  classes have that spell.
* When leveling you will get the highest gain of all classes.
* The same also applies to AC, you get the best Thac0 for each class.
* When increasing stats, you will get the primary stat (+2) bonus of
  all your classes. If you have two classes with the same primary 
  stat, the bonus is only given once.
* When you gain a skill, if you have two classes with that skill and
  there are different CP costs, you will pay the lower.
* 'Who' and 'Group' will show a 1 letter abbreviation for your class
  and the total number of classes you have. 'whois' will show
  all of your classes.

To remort follow the following steps:

1. Make sure your a LEGEND (Level 202, see help LEGEND).

2. Remove any eq and either donate/sac/auction it all or put some in a locker.

3. Go to the Priest in Midgaard (2s3en2w from recall), type 'remort'
   and follow instructions, you will be re-guided through the ceration process.
   
Please take a few seconds to read very carefully the messages that the mud
gives you during the remort process. Unless caused by a bug, any mistakes 
you make during remort will not be reversed by an imm. 

SEE ALSO HELP LEGEND

~
>>>>> END HELP FILE <<<<<<

Markanth : markanth@spaceservices.net
Devil's Lament : spaceservices.net port 3778
Web Page : http://spaceservices.net/~markanth/

