1stMud4.5.3/
1stMud4.5.3/backup/
1stMud4.5.3/bin/
1stMud4.5.3/bin/extras/
1stMud4.5.3/data/i3/
1stMud4.5.3/doc/1stMud/
1stMud4.5.3/doc/Diku/
1stMud4.5.3/doc/MPDocs/
1stMud4.5.3/doc/Rom/
1stMud4.5.3/notes/
/**************************************************************************
*  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-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "vnums.h"
#include "interp.h"

Do_Fun(do_remort)
{
	Descriptor *d;
	CharData *mob;
	void send_race_info(Descriptor *);
	void send_class_info(Descriptor *);

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

	if (ch->in_room->guild == -1 || !is_class(ch, ch->in_room->guild))
	{
		chprintlnf(ch, "You must be at your class%s guild to do that.",
				   IsRemort(ch) ? "(s)" : "");
		return;
	}

	for (mob = ch->in_room->person_first; mob; mob = mob->next_in_room)
	{
		if (IsNPC(mob) && IsSet(mob->act, ACT_TRAIN | ACT_GAIN))
			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 (ch->Class[CLASS_COUNT] == MAX_REMORT
		|| ch->Class[CLASS_COUNT] == top_class)
	{
		chprintln(ch, "You can't remort any more!");
		return;
	}

	if (IsQuester(ch) || Gquester(ch))
	{
		chprintln(ch, "Don't you want to finish your quest first?");
		return;
	}

	if (!check_worth(ch, 500000, VALUE_GOLD)
		|| ch->pcdata->quest.points < 500)
	{
		chprintln(ch,
				  "You need 500,000 gold and 500 quest points to remort.");
		return;
	}

	if (ch->pcdata->confirm_remort)
	{
		if (!NullStr(argument))
		{
			chprintln(ch, "Remort status removed.");
			ch->pcdata->confirm_remort = false;
			return;
		}
		else
		{
			dwraplnf(ch->desc,
					 "You have chosen to remort.  You will now be dropped in at the %s"
					 " selection section of character creation, and will be allowed recreate"
					 " your character with an additional {Cclass{x%s and {Gbonuses{x."
					 NEWLINE
					 "In the unlikely event that you are disconnected or the Mud"
					 " crashes while you are creating your character, log back on and write a"
					 " note to an immortal who will retrieve your backup.",
					 !ch->pcdata->stay_race ? "{BRACE{x" : "{CCLASS{x",
					 !ch->pcdata->stay_race ? ", {brace{x" : "");
			wiznet("$N has remorted.", ch, NULL, 0, true, 0);
			char_from_room(ch);
			char_to_room(ch, get_room_index(ROOM_VNUM_LIMBO));
			UnLink(ch, char, next, prev);

			UnLink(ch, player, next_player, prev_player);

			chprintln(ch, NEWLINE "Now beginning the remorting process.");

			if (!ch->pcdata->stay_race)
			{
				send_race_info(ch->desc);
				chprint(ch,
						NEWLINE
						"What is your race (help for more information)?");
				d->connected = CON_GET_NEW_RACE;
			}
			else
			{
				ch->pcdata->points = ch->race->points;

				send_class_info(ch->desc);
				chprintln(ch, "What is your next class? ");
				d->connected = CON_GET_NEW_CLASS;
			}
			return;
		}
	}

	if (!NullStr(argument))
	{
		chprintln(ch, "Just type remort.  No argument.");
		return;
	}
	dwraplnf(ch->desc,
			 "{RTyping {Gremort{R with an argument will undo remort status."
			 "  Remorting is {Wnot reversable{R, make sure you read help REMORT"
			 " and have an idea of what {Cclass%s{R you want to remort into."
			 "  Type {Gremort{R again to confirm this command.{x",
			 !ch->pcdata->stay_race ? "and {Brace" : "");
	ch->pcdata->confirm_remort = true;
	if (!ch->pcdata->stay_race)
		chprintln(ch,
				  "{f{WWARNING{x{R: IF YOU CHOOSE A RACE DIFFERENT FROM YOUR RACE NOW YOU WILL BE THAT RACE {fFOREVER{x{R.{x");
	wiznet("$N is contemplating remorting.", ch, NULL, 0, false,
		   get_trust(ch));
}

void finish_remort(CharData * ch)
{
	int sn, b = lvl_bonus(ch);
	ObjData *obj, *obj_next;
	AffectData *af, *af_next;

	d_println(ch->desc, "Sucessful Remort!");
	wiznet("Sucessful Remort! $N sighted.", ch, NULL, 0, false, 0);

	Link(ch, char, next, prev);

	Link(ch, player, next_player, prev_player);

	for (af = ch->affect_first; af != NULL; af = af_next)
	{
		af_next = af->next;
		affect_remove(ch, af);
	}
	for (obj = ch->carrying_first; obj != NULL; obj = obj_next)
	{
		obj_next = obj->next_content;
		if (obj->wear_loc != WEAR_NONE)
		{
			unequip_char(ch, obj);
		}
	}
	ch->trust = ch->level;
	ch->level = 1;
	ch->exp = 0;
	ch->pcdata->points = 0;
	deduct_cost(ch, 500000, VALUE_GOLD);
	ch->pcdata->quest.points -= 500;
	ch->max_hit = 100 * b;
	ch->max_mana = 100 * b;
	ch->max_move = 100 * b;
	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 * b;
	ch->practice = 7 * b;
	ch->exp = exp_per_level(ch, ch->pcdata->points);
	reset_char(ch);

	if (ch->pet != NULL)
	{
		nuke_pets(ch);
		ch->pet = NULL;
	}

	for (sn = 0; sn < top_skill; 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;
		}
	}
	char_from_room(ch);
	char_to_room(ch, get_room_index(ROOM_VNUM_SCHOOL));
	ch->desc->connected = CON_PLAYING;
	act
		("You are brought back to reality, and you feel quite different now...",
		ch, NULL, NULL, TO_CHAR);
	do_function(ch, &do_outfit, "");
	save_char_obj(ch);
	mud_info.stats.remorts++;
}

bool can_use_skpell(CharData * ch, int sn)
{
	int i;

	if (sn < 0 || sn >= top_skill)
		return false;

	if (IsNPC(ch))
		return true;

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

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

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		if (ch->level >= skill_table[sn].skill_level[ch->Class[i]])
			return true;

	return false;
}

bool has_spells(CharData * ch)
{
	int i;

	if (IsNPC(ch))
		return false;
	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		if (class_table[i].fMana)
			return true;
	return false;
}

bool is_class(CharData * ch, int Class)
{
	int i;

	if (IsNPC(ch))
		return false;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (ch->Class[i] == Class)
			return true;
	}

	return false;
}

bool is_same_class(CharData * ch, CharData * victim)
{
	int i, jClass;

	if (IsNPC(ch) || IsNPC(victim))
		return false;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		for (jClass = 0; victim->Class[jClass] != -1; jClass++)
			if (ch->Class[i] == victim->Class[jClass])
				return true;
	}

	return false;
}

int prime_class(CharData * ch)
{
	int slot = 0;

	if (!IsNPC(ch))
		slot = Range(0, ch->pcdata->prime_class, ch->Class[CLASS_COUNT] - 1);

	return ch->Class[slot];
}

int current_class(CharData * ch)
{
	return ch->Class[ch->Class[CLASS_COUNT] - 1];
}

int class_slot(CharData * ch, int pclass)
{
	int iClass;

	if (IsNPC(ch))
		return 0;

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

	return -1;
}

int number_classes(CharData * ch)
{
	int i;

	if (IsNPC(ch))
		return number_range(1, MAX_REMORT);

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		;

	return i;
}

char *class_long(CharData * ch)
{
	static char buf[512];
	int i;

	buf[0] = '\0';
	if (IsNPC(ch))
		return "Mobile";
	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		strcat(buf, "/");
		strcat(buf, ClassName(ch, ch->Class[i]));
	}
	return buf + 1;
}

char *class_who(CharData * ch)
{
	static char buf[512];

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

	if (IsRemort(ch))
		sprintf(buf, "%2.2s+%d", ClassName(ch, prime_class(ch)),
				ch->Class[CLASS_COUNT] - 1);
	else
		sprintf(buf, "%4.4s", ClassName(ch, prime_class(ch)));
	return buf;
}

char *class_short(CharData * ch)
{
	static char buf[512];
	int i;

	buf[0] = '\0';
	if (IsNPC(ch))
		return "Mob";
	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		strcat(buf, "/");
		sprintf(buf + strlen(buf), "%4.4s", ClassName(ch, ch->Class[i]));
	}
	return buf + 1;
}

char *class_numbers(CharData * ch)
{
	static char buf[512];
	char buf2[10];
	int i;

	buf[0] = '\0';

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

	for (i = 0; i < MAX_MCLASS; i++)
	{
		strcat(buf, " ");
		sprintf(buf2, "%d", ch->Class[i]);
		strcat(buf, buf2);
	}
	return buf + 1;
}

int skill_level(CharData * ch, int sn)
{
	int i;
	int tempskill = 999;

	if (sn < 0 || sn >= top_skill)
		return MAX_LEVEL + 1;

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

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

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		tempskill = Min(tempskill, skill_table[sn].skill_level[ch->Class[i]]);

	return tempskill == 999 ? LEVEL_IMMORTAL : tempskill;
}

int skill_rating(CharData * ch, int sn)
{
	int i;
	int temprate = 999;

	if (sn < 0 || sn >= top_skill)
		return 0;

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

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

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (skill_table[sn].rating[ch->Class[i]] < 1)
			continue;

		temprate = Min(temprate, skill_table[sn].rating[ch->Class[i]]);
	}
	return temprate == 999 ? 0 : temprate;
}

int group_rating(CharData * ch, int gn)
{
	int i;
	int temprate = 999;

	if (gn < 0 || gn >= top_group)
		return 0;

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

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

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (group_table[gn].rating[ch->Class[i]] < 1)
			continue;

		temprate = Min(temprate, group_table[gn].rating[ch->Class[i]]);
	}
	return temprate == 999 ? 0 : temprate;
}

int get_hp_gain(CharData * ch)
{
	int i = 0;
	int gain = 0;
	int count = 0;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		gain =
			Max(gain,
				number_range(class_table[ch->Class[i]].hp_min,
							 class_table[ch->Class[i]].hp_max));
		count++;
	}
	return number_range(gain, gain + count);
}

int hp_max(CharData * ch)
{
	int i;
	int tmp = 0;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		tmp = Max(tmp, class_table[ch->Class[i]].hp_max);

	return tmp;
}

bool is_prime_stat(CharData * ch, int stat)
{
	int i = 0;

	if (IsNPC(ch))
		return true;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (class_table[ch->Class[i]].attr_prime == stat)
			return true;
	}
	return false;
}

void add_default_groups(CharData * ch)
{
	int i = 0;

	if (IsNPC(ch))
		return;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		if (class_table[ch->Class[i]].default_group != NULL)
			group_add(ch, class_table[ch->Class[i]].default_group, false);

	ch->pcdata->points += i > 0 ? 50 : 40;
}

void add_base_groups(CharData * ch)
{
	int i = 0;

	if (IsNPC(ch))
		return;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		if (class_table[ch->Class[i]].base_group != NULL)
			group_add(ch, class_table[ch->Class[i]].base_group, false);
}

bool check_base_group(CharData * ch, int gn)
{
	int i = 0;

	if (IsNPC(ch))
		return false;

	if (gn < 0 || gn >= top_group)
		return false;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (class_table[ch->Class[i]].base_group == NULL)
			continue;

		if (group_lookup(class_table[ch->Class[i]].base_group) == gn)
			return true;
	}
	return false;
}

bool is_base_skill(CharData * ch, int sn)
{
	int i = 0;
	int gn, x;

	if (IsNPC(ch))
		return false;

	if (sn < 0 || sn >= top_skill)
		return false;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
	{
		if (class_table[ch->Class[i]].base_group == NULL)
			continue;

		if ((gn = group_lookup(class_table[ch->Class[i]].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;
}

int get_thac00(CharData * ch)
{
	int temp = 0, i = 0;

	if (IsNPC(ch))
		return 0;
	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		temp = Max(temp, class_table[ch->Class[i]].thac0_00);

	return temp;
}

int get_thac32(CharData * ch)
{
	int temp = 999, i = 0;

	if (IsNPC(ch))
		return 0;
	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		temp = Min(temp, class_table[ch->Class[i]].thac0_32);

	return temp;
}

int class_mult(CharData * ch)
{
	int temp = 999, i = 0;

	if (IsNPC(ch))
		return 0;

	for (i = 0; i < ch->Class[CLASS_COUNT]; i++)
		temp = Min(temp, ch->race->class_mult[ch->Class[i]]);

	return temp;
}

int lvl_bonus(CharData * ch)
{
	float adlev, inclev;
	int cntr;

	adlev = ch->Class[CLASS_COUNT];
	inclev = .09;
	for (cntr = 1; cntr < ch->level; cntr++)
	{
		adlev += .9;
		adlev += inclev;
		inclev += .009;
	}
	return (int) (adlev + inclev);
}

bool is_race_skill(CharData * ch, int sn)
{
	int i;

	if (sn < 0 || sn >= top_skill)
		return false;

	for (i = 0; i < MAX_RACE_SKILL; 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(CharData * ch, int sn)
{
	if (sn < 0 || sn >= top_skill)
		return false;

	if (ch->deity == NULL || IsNPC(ch))
		return false;

	if (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;
}

Do_Fun(do_prime)
{
	int iClass, iSlot;

	if (NullStr(argument))
	{
		cmd_syntax(ch, NULL, n_fun, "<class>", NULL);
		chprintln(ch,
				  "It costs {R5{x trivia points to change your prime class.");
	}

	if ((iClass = class_lookup(argument)) == -1)
	{
		chprintln(ch, "No such class!");
		return;
	}

	if ((iSlot = class_slot(ch, iClass)) == -1)
	{
		chprintlnf(ch, "You aren't part %s!", class_table[iClass].name);
		return;
	}

	if (iSlot == ch->pcdata->prime_class)
	{
		chprintlnf(ch, "Your prime class is already %s.",
				   class_table[iClass].name);
		return;
	}

	if (ch->pcdata->trivia < 5)
	{
		chprintln(ch,
				  "It costs {R5{x trivia points to change your prime class.");
		return;
	}

	ch->pcdata->prime_class = iSlot;
	ch->pcdata->trivia -= 5;
	chprintlnf(ch,
			   "Your prime class is now %s, and are {R5{x trivia points lighter.",
			   class_table[iClass].name);
	return;
}