1stMUD/corefiles/
1stMUD/gods/
1stMUD/log/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"

CH_CMD(do_remor)
{
	chprintln(ch, "If you want to REMORT, you must spell it out.");
	return;
}

CH_CMD(do_remort)
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *mob;
	AFFECT_DATA *af, *af_next;
	char buf[MAX_INPUT_LENGTH];
	int x, sn;

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

	/*
	 * check for priest or special mob
	 */
	for (mob = ch->in_room->first_person; mob; mob = mob->next_in_room)
	{
		if (IS_NPC(mob) && IS_SET(mob->act, ACT_IS_HEALER))	/* setup to do at healer's for now */
			break;
	}

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

	if (ch->level != calc_max_level(ch))
	{
		chprintlnf(ch, "You must be a %s to remort.",
				   high_level_name(calc_max_level(ch), TRUE));
		return;
	}

	if (number_classes(ch) == MAX_REMORT || number_classes(ch) == maxClass)
	{
		chprintln(ch, "You can't remort any more!");
		return;
	}

/* Remove high level eq since we're going back to level 1 */
	for (x = 0; x < MAX_WEAR; x++)
	{
		if (get_eq_char(ch, x) != NULL)
		{
			chprintln(ch,
					  "Remove all of your eq first. (heal uncurse for cursed items)");
			return;
		}
	}

	if (ch->pcdata->confirm_remort)
	{
		if (argument[0] != '\0')
		{
			chprintln(ch, "Remort status removed.");
			ch->pcdata->confirm_remort = FALSE;
			return;
		}
		else
		{
			chprintlnf(ch,
					   "You have chosen to remort.  You will now be dropped in at the %s",
					   !ch->pcdata->stay_race ? "RACE" : "CLASS");
			chprintln(ch,
					  "selection section of character creation, and will be allowed recreate");
			chprintln(ch,
					  "your character with an additional class and bonuses.\n\r");
			chprintln(ch,
					  "In the unlikely event that you are disconnected or the MUD");
			chprintln(ch,
					  "crashes while you are creating your character, log back on and write a");
			chprintln(ch, "note to 'immortal' who will retrieve your backup.");
			wiznet("$N has remorted.", ch, NULL, 0, 0, 0);
			for (af = ch->first_affect; af != NULL; af = af_next)
			{
				af_next = af->next;
				affect_remove(ch, af);
			}
			SET_BIT(ch->act, PLR_REMORT);
			SET_BIT(ch->comm, COMM_QUIET);
			char_from_room(ch);
			char_to_room(ch, get_room_index(ROOM_VNUM_LIMBO));
			ch->level = 1;
			ch->exp = 0;
			ch->pcdata->points = 0;
			ch->max_hit = 100 * (number_classes(ch) + 1);
			ch->max_mana = 100 * (number_classes(ch) + 1);
			ch->max_move = 100 * (number_classes(ch) + 1);
			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) + 1);
			ch->practice = 7 * (number_classes(ch) + 1);
			ch->exp = exp_per_level(ch, ch->pcdata->points);
			reset_char(ch);
			/* nuke any high level pets */
			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 < maxSkill; sn++)
			{
				if (ch->pcdata->learned[sn] > 0
					&& ch->pcdata->learned[sn] < 100)
				{
					if (is_race_skill(ch, sn) && !ch->pcdata->stay_race)
						ch->pcdata->learned[sn] = 0;
					else
						ch->pcdata->learned[sn] = 1;
				}
			}
			/* send char to race selection, customize this as you see fit */
			chprintln(ch, "\n\rNow beginning the remorting process.\n\r");

			if (!ch->pcdata->stay_race)
			{
				RACE_DATA *race;

				chprintln(ch, "The following races are available:");
				for (race = race_first; race != NULL; race = race->next)
				{
					if (!race->pc_race)
						break;
					chprint(ch, race->name);
					chprint(ch, " ");
				}
				chprint
					(ch, "\n\rWhat is your race (help for more information)?");
				d->connected = CON_GET_NEW_RACE;
			}
			else
			{
				int iClass;

				ch->pcdata->points = ch->race->points;

				sprintf(buf, "Select class number %d [ ",
						(number_classes(ch) + 1));
				for (iClass = 0; iClass < maxClass; iClass++)
				{
					if (is_class(ch, iClass))
						continue;
					strcat(buf, class_table[iClass].name);
				}
				strcat(buf, "]: ");
				chprint(ch, buf);
				d->connected = CON_GET_NEW_CLASS;
			}
			return;
		}
	}

	if (argument[0] != '\0')
	{
		chprintln(ch, "Just type remort.  No argument.");
		return;
	}
	chprintln(ch, "Typing remort with an argument will undo remort status.");
	chprintln(ch,
			  "Remorting is not reversable, make sure you read help REMORT");
	chprintln(ch, "and have an idea of what class you want to remort into.");
	chprintln(ch, "Type remort again to confirm this command.");
	ch->pcdata->confirm_remort = TRUE;
	if (!ch->pcdata->stay_race)
		chprintln(ch,
				  "WARNING: IF YOU CHOOSE A RACE DIFFERENT FROM YOUR RACE NOW YOU WILL BE THAT RACE FOREVER.\n\r");
	wiznet("$N is contemplating remorting.", ch, NULL, 0, 0, get_trust(ch));
}

/* Use for things like:
 *  ch->level >= skill_table[sn].skill_level[ch->Class]
 */
bool can_use_skpell(CHAR_DATA * ch, int sn)
{
	int iClass;

	if (IS_NPC(ch))
		return TRUE;

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

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

	for (iClass = 0; ch->Class[iClass] != -1; iClass++)
		if (ch->level >= skill_table[sn].skill_level[ch->Class[iClass]])
			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, jClass;

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

	for (iClass = 0; ch->Class[iClass] != -1; iClass++)
	{
		for (jClass = 0; victim->Class[jClass] != -1; jClass++)
			if (ch->Class[iClass] == victim->Class[jClass])
				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]].name[0],
				number_classes(ch) - 1);
	else
		sprintf(buf, "%3.3s", class_table[ch->Class[0]].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, FORMATF("%3.3s", class_table[ch->Class[iClass]].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;

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

	for (iClass = 0; ch->Class[iClass] != -1; iClass++)
		tempskill =
			UMIN(tempskill, skill_table[sn].skill_level[ch->Class[iClass]]);

	return tempskill == 999 ? LEVEL_IMMORTAL : 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;

	if (is_deity_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 == 999 ? 0 : 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;

	if (is_deity_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 == 999 ? 0 : 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);
}

int hp_max(CHAR_DATA * ch)
{
	int iClass;
	int tmp = 0;

	for (iClass = 0; ch->Class[iClass] != -1; iClass++)
		tmp = UMAX(tmp, class_table[ch->Class[iClass]].hp_max);

	return tmp;
}

/* 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
   used in creation so base groups never get dropped */
bool check_base_group(CHAR_DATA * ch, int gn)
{
	int iClass = 0;

	if (IS_NPC(ch))
		return FALSE;

	if (gn < 0 || gn >= maxGroup)
		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 
    Used in creation so base skills never get dropped */
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 >= maxSkill)
		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 = 0, iClass = 0;

	if (IS_NPC(ch))
		return 0;
	for (iClass = 0; ch->Class[iClass] != -1; iClass++)
		temp = UMAX(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 */
int class_mult(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, ch->race->class_mult[ch->Class[iClass]]);

	return temp;
}

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

/* Returns TRUE if skill number is a race skill 
   If you have clan skills you can do something simalar
   for those */
bool is_race_skill(CHAR_DATA * ch, int sn)
{
	int i;

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

bool is_deity_skill(CHAR_DATA * ch, int sn)
{
	if (ch->deity == NULL || IS_NPC(ch))
		return FALSE;

	if (IS_NULLSTR(ch->deity->skillname))
		return FALSE;

	if (skill_lookup(ch->deity->skillname) == sn)
		return TRUE;

	if (group_lookup(ch->deity->skillname) == sn)
		return TRUE;

	return FALSE;
}