/**************************************************************************
* 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->people; 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;
}
/* 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_HERO)
{
chprintln (ch, "You must be a HERO to remort.");
return;
}
if (number_classes (ch) == MAX_REMORT)
{
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->affected; 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)
{
if (is_race_skill (ch, sn) && !ch->pcdata->stay_race)
ch->pcdata->learned[sn] = 0;
else if (ch->pcdata->learned[sn] == 100)
ch->pcdata->learned[sn] = 100;
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)
{
int race;
chprintln (ch, "The following races are available:");
for (race = 1; race_table[race].name != NULL; race++)
{
if (!race_table[race].pc_race)
break;
chprint (ch, race_table[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 = race_table[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;
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;
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++)
tempskill =
UMIN (tempskill, skill_table[sn].skill_level[ch->Class[iClass]]);
return tempskill == 999 ? 0 : 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++)
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;
for (iClass = 0; ch->Class[iClass] != -1; iClass++)
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 = 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 */
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, race_table[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 (race_table[ch->race].skills[i] == NULL)
continue;
if (skill_lookup (race_table[ch->race].skills[i]) == sn)
return TRUE;
if (group_lookup (race_table[ch->race].skills[i]) == sn)
return TRUE;
}
return FALSE;
}