/************************************************************************** * 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-2003 by Ryan Jennings * * http://1stmud.dlmud.com/ <r-jenn@shaw.ca> * ***************************************************************************/ #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, (wloc_t) x) != NULL) { chprintln(ch, "Remove all of your eq first. (heal uncurse for cursed items)"); return; } } if (ch->pcdata->confirm_remort) { if (!IS_NULLSTR(argument)) { 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 (!IS_NULLSTR(argument)) { 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 1 + 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; }