6D/
6D/area/
6D/boards/
6D/city/
6D/color/
6D/corpses/
6D/councils/
6D/htowns/
6D/news/
6D/specials/
6D/src/specials/
6D/src/trades/
//

#include "h/mud.h"
#include "h/files.h"
#include "h/languages.h"
#include "h/key.h"
#ifndef WIN32
#include <unistd.h>
#endif

int                     get_risflag(char *flag);

struct class_type      *class_table[MAX_CLASS];
char                   *title_table[MAX_CLASS][MAX_LEVEL + 1][2];

const char             *const npc_class[MAX_NPC_CLASS] = {
  "shaman", "druid", "mage", "bat", "warrior", "crusader",
  "thief", "dragon lord", "black", "gold", "silver", "red", "blue", "hellspawn",
  "angel", "vampire", "monk", "bard", "shadowknight", "psionic", "hero",
  "twoheaded", "balrog", "sorcerer", "rogue", "wolf", "owl", "fish", "bear",
  "gryphon", "treant", "beastmaster"
};

void free_all_classes(void)
{
  int                     hash;

  for(hash = 0; hash < MAX_CLASS; hash++)
  {
    if(!class_table[hash])
      continue;
    if(class_table[hash]->who_name)
      STRFREE(class_table[hash]->who_name);
    if(class_table[hash]->filename)
      STRFREE(class_table[hash]->filename);
    DISPOSE(class_table[hash]);
  }
  return;
}

/* Create a new class online. -Shaddai */
bool create_new_class(int Class, char *argument)
{
  int                     i;

  if(Class >= MAX_CLASS || class_table[Class] == NULL || !VLD_STR(argument))
    return FALSE;

  if(class_table[Class]->who_name)
    STRFREE(class_table[Class]->who_name);
  if(class_table[Class]->filename)
    STRFREE(class_table[Class]->filename);
  if(argument[0] != '\0')
    argument[0] = UPPER(argument[0]);
  class_table[Class]->who_name = STRALLOC(argument);
  class_table[Class]->filename = STRALLOC(argument);
  xCLEAR_BITS(class_table[Class]->affected);
  class_table[Class]->attr_prime = 0;
  class_table[Class]->attr_second = 0;
  class_table[Class]->attr_deficient = 0;
  class_table[Class]->race_restriction = 0;
  class_table[Class]->combo_restriction = 0;
  class_table[Class]->resist = 0;
  class_table[Class]->suscept = 0;
  class_table[Class]->skill_adept = 0;
  class_table[Class]->thac0_00 = 0;
  class_table[Class]->thac0_32 = 0;
  class_table[Class]->hp_min = 0;
  class_table[Class]->hp_max = 0;
  class_table[Class]->mana_min = 0;
  class_table[Class]->mana_max = 0;
  class_table[Class]->move_min = 0;
  class_table[Class]->move_max = 0;
  class_table[Class]->starting = FALSE; /* Default to not a starting class */
  class_table[Class]->exp_base = 0;
  class_table[Class]->craft_base = 0;
  for(i = 0; i < MAX_LEVEL; i++)
  {
    title_table[Class][i][0] = STRALLOC("Not set.");
    title_table[Class][i][1] = STRALLOC("Not set.");
  }
  return TRUE;
}

void write_class_list(void)
{
  char                    classlist[256];
  FILE                   *fpList;
  int                     i;

  snprintf(classlist, 256, "%s%s", CLASSDIR, CLASS_LIST);

  if((fpList = FileOpen(classlist, "w")) == NULL)
  {
    bug("Can't open %s %s Class list for writing.", __FUNCTION__, CLASS_LIST);
    perror(CLASS_LIST);
    return;
  }
  for(i = 0; i < MAX_PC_CLASS; i++)
//    fprintf(fpList, "%s%s.class\n", CLASSDIR, class_table[i]->filename);  Volk - bugging the file LOLOL
    fprintf(fpList, "%s.class\n", class_table[i]->filename);
  fprintf(fpList, "$\n");
  FileClose(fpList);
}

/* Edit class information -Thoric */
void do_setclass(CHAR_DATA *ch, char *argument)
{
  char                    arg1[MIL];
  char                    arg2[MIL];
  struct class_type      *Class;
  int                     cl, value, i;

  set_char_color(AT_PLAIN, ch);
  smash_tilde(argument);
  argument = one_argument(argument, arg1);
  argument = one_argument(argument, arg2);
  if(!VLD_STR(arg1))
  {
    send_to_char("Syntax: setclass <class> <field> <value>\r\n", ch);
    send_to_char("Syntax: setclass <class> create\r\n", ch);
    send_to_char("\r\nField being one of:\r\n", ch);
    send_to_char("  name filename prime thac0 thac32 nocombo\r\n", ch);
    send_to_char("  hpmin hpmax manamin manamax expbase mtitle ftitle\r\n", ch);
    send_to_char("  second, deficient affected resist suscept\r\n", ch);
    send_to_char("  race starting reclass1 reclass2 reclass3\r\n", ch);
    return;
  }
  if(is_number(arg1) && (cl = atoi(arg1)) >= 0 && cl < MAX_CLASS)
    Class = class_table[cl];
  else
  {
    Class = NULL;
    for(cl = 0; cl < MAX_CLASS && class_table[cl]; cl++)
    {
      if(!class_table[cl]->who_name)
        continue;
      if(!str_cmp(class_table[cl]->who_name, arg1))
      {
        Class = class_table[cl];
        break;
      }
    }
  }
  if(!str_cmp(arg2, "create") && Class)
  {
    send_to_char("That class already exists!\r\n", ch);
    return;
  }
  if(!Class && str_cmp(arg2, "create"))
  {
    send_to_char("No such class.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "save"))
  {
    DESCRIPTOR_DATA        *d;

    write_class_file(cl);
    send_to_char("&CUpdating class for all players now.\r\n", ch);
    for(d = first_descriptor; d; d = d->next)
    {
      if(d->character)
        update_aris(d->character);
    }
    return;
  }
  if(!str_cmp(arg2, "create"))
  {
    if(MAX_PC_CLASS >= MAX_CLASS)
    {
      send_to_char("You need to up MAX_CLASS in mud and make clean.\r\n", ch);
      return;
    }
    if((create_new_class(MAX_PC_CLASS, arg1)) == FALSE)
    {
      send_to_char("Couldn't create a new class.\r\n", ch);
      return;
    }
    write_class_file(MAX_PC_CLASS);
    MAX_PC_CLASS++;
    write_class_list();
    send_to_char("Done.\r\n", ch);
    return;
  }

  if(!argument)
  {
    send_to_char("You must specify an argument.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "name"))
  {
    if(!VLD_STR(argument))
    {
      send_to_char("Can't set a class name to nothing.\r\n", ch);
      return;
    }
    if(VLD_STR(Class->who_name))
      STRFREE(Class->who_name);
    Class->who_name = STRALLOC((argument));
    send_to_char("Class name is set.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "filename"))
  {
    if(!VLD_STR(argument))
    {
      send_to_char("Can't set a filename to nothing.\r\n", ch);
      return;
    }
    if(VLD_STR(Class->filename))
      STRFREE(Class->filename);
    Class->filename = STRALLOC((argument));
    send_to_char("Filename is set.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "starting"))
  {
    Class->starting = !Class->starting;

    ch_printf(ch, "That class is now a %s class.\r\n", Class->starting ? "starting" : "non-starting");
    return;
  }

  // Start of the nocombo aurgument! -Taon

  if(!str_cmp(arg2, "nocombo"))
  {
    for(i = 0; i < MAX_CLASS; i++)
    {
      if(class_table[i] && VLD_STR(class_table[i]->who_name))
      {
        if(!str_cmp(argument, class_table[i]->who_name))
        {
          TOGGLE_BIT(Class->combo_restriction, 1 << i);
          send_to_char("Done!\r\n", ch);
          send_to_char("Note this still isnt complete, as far as the nanny function is concerned!\r\n", ch);
          return;
        }
      }
    }
    return;
  }
  if(!str_cmp(arg2, "race"))
  {
    for(i = 0; i < MAX_RACE; i++)
    {
      if(!str_cmp(argument, race_table[i]->race_name))
      {
        TOGGLE_BIT(Class->race_restriction, 1 << i);  /* k, that's boggling */
        send_to_char("Done.\r\n", ch);
        return;
      }
    }
    send_to_char("No such race.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "reclass1"))
  {
    Class->reclass1 = 0;
    for(i = 0; i < MAX_CLASS; i++)
    {
      if(class_table[i] && VLD_STR(class_table[i]->who_name))
      {
        if(!str_cmp(argument, class_table[i]->who_name))
        {
          Class->reclass1 = i;
          send_to_char("Done.\r\n", ch);
          return;
        }
      }
    }
    send_to_char("No such class. Reclass1 cleared.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "reclass2"))
  {
    Class->reclass2 = 0;
    for(i = 0; i < MAX_CLASS; i++)
    {
      if(class_table[i] && VLD_STR(class_table[i]->who_name))
      {
        if(!str_cmp(argument, class_table[i]->who_name))
        {
          Class->reclass2 = i;
          send_to_char("Done.\r\n", ch);
          return;
        }
      }
    }
    send_to_char("No such class. Reclass2 cleared.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "reclass3"))
  {
    Class->reclass3 = 0;
    for(i = 0; i < MAX_CLASS; i++)
    {
      if(class_table[i] && VLD_STR(class_table[i]->who_name))
      {
        if(!str_cmp(argument, class_table[i]->who_name))
        {
          Class->reclass3 = i;
          send_to_char("Done.\r\n", ch);
          return;
        }
      }
    }
    send_to_char("No such class. Reclass3 cleared.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "second"))
  {
    int                     x = get_atype(argument);

    if(x < APPLY_STR || (x > APPLY_CON && x != APPLY_LCK && x != APPLY_CHA))
      send_to_char("Invalid second attribute!\r\n", ch);
    else
    {
      Class->attr_second = x;
      send_to_char("Done.\r\n", ch);
    }
    return;
  }

  if(!str_cmp(arg2, "affected"))
  {
    if(!VLD_STR(argument))
    {
      send_to_char("Usage: setclass <class> affected <flag> [flag]...\r\n", ch);
      return;
    }
    while(VLD_STR(argument))
    {
      argument = one_argument(argument, arg2);
      value = get_aflag(arg2);
      if(value < 0 || value > MAX_BITS)
        ch_printf(ch, "Unknown flag: %s\r\n", arg2);
      else
        xTOGGLE_BIT(Class->affected, value);
    }
    send_to_char("Done.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "resist"))
  {
    if(!VLD_STR(argument))
    {
      send_to_char("Usage: setclass <class> resist <flag> [flag]...\r\n", ch);
      return;
    }
    while(VLD_STR(argument))
    {
      argument = one_argument(argument, arg2);
      value = get_risflag(arg2);
      if(value < 0 || value > 31)
        ch_printf(ch, "Unknown flag: %s\r\n", arg2);
      else
        TOGGLE_BIT(Class->resist, 1 << value);
    }
    send_to_char("Done.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "suscept"))
  {
    if(!VLD_STR(argument))
    {
      send_to_char("Usage: setclass <class> suscept <flag> [flag]...\r\n", ch);
      return;
    }
    while(VLD_STR(argument))
    {
      argument = one_argument(argument, arg2);
      value = get_risflag(arg2);
      if(value < 0 || value > 31)
        ch_printf(ch, "Unknown flag: %s\r\n", arg2);
      else
        TOGGLE_BIT(Class->suscept, 1 << value);
    }
    send_to_char("Done.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "deficient"))
  {
    int                     x = get_atype(argument);

    if(x < APPLY_STR || (x > APPLY_CON && x != APPLY_LCK && x != APPLY_CHA))
      send_to_char("Invalid deficient attribute!\r\n", ch);
    else
    {
      Class->attr_deficient = x;
      send_to_char("Done.\r\n", ch);
    }
    return;
  }
  if(!str_cmp(arg2, "prime"))
  {
    int                     x = get_atype(argument);

    if(x < APPLY_STR || (x > APPLY_CON && x != APPLY_LCK && x != APPLY_CHA))
      send_to_char("Invalid prime attribute!\r\n", ch);
    else
    {
      Class->attr_prime = x;
      send_to_char("Done.\r\n", ch);
    }
    return;
  }
  if(!str_cmp(arg2, "thac0"))
  {
    Class->thac0_00 = atoi(argument);
    send_to_char("Done.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "thac32"))
  {
    Class->thac0_32 = atoi(argument);
    send_to_char("Done.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "hpmin"))
  {
    Class->hp_min = atoi(argument);
    send_to_char("Hp min set.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "hpmax"))
  {
    Class->hp_max = atoi(argument);
    send_to_char("Hp max set.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "manamin"))
  {
    Class->mana_min = atoi(argument);
    send_to_char("Move min set.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "manamax"))
  {
    Class->mana_max = atoi(argument);
    send_to_char("Move max set.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "movemin"))
  {
    Class->move_min = atoi(argument);
    send_to_char("Move min set.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "movemax"))
  {
    Class->move_max = atoi(argument);
    send_to_char("Move max set.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "expbase"))
  {
    Class->exp_base = atoi(argument);
    send_to_char("Done.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "craftbase"))
  {
    Class->craft_base = atoi(argument);
    send_to_char("Craft base is now set.\r\n", ch);
    return;
  }

  if(!str_cmp(arg2, "mtitle"))
  {
    char                    arg3[MIL];
    int                     x;

    argument = one_argument(argument, arg3);
    if(arg3[0] == '\0' || argument[0] == '\0')
    {
      send_to_char("Syntax: setclass <class> mtitle <level> <title>\r\n", ch);
      return;
    }
    if(!VLD_STR(argument))
    {
      send_to_char("What would you like to set the mtitle_table to?\r\n", ch);
      return;
    }
    if((x = atoi(arg3)) < 0 || x > MAX_LEVEL)
    {
      send_to_char("Invalid level.\r\n", ch);
      return;
    }
    STRFREE(title_table[cl][x][0]);
    title_table[cl][x][0] = STRALLOC(argument);
    send_to_char("Done.\r\n", ch);
    return;
  }
  if(!str_cmp(arg2, "ftitle"))
  {
    char                    arg3[MIL];
    int                     x;

    argument = one_argument(argument, arg3);
    if(arg3[0] == '\0' || argument[0] == '\0')
    {
      send_to_char("Syntax: setclass <class> ftitle <level> <title>\r\n", ch);
      return;
    }
    if(!VLD_STR(argument))
    {
      send_to_char("What would you like to set the ftitle_table to?\r\n", ch);
      return;
    }
    if((x = atoi(arg3)) < 0 || x > MAX_LEVEL)
    {
      send_to_char("Invalid level.\r\n", ch);
      return;
    }
    STRFREE(title_table[cl][x][1]);
    title_table[cl][x][1] = STRALLOC(argument);
    send_to_char("Done.\r\n", ch);
    return;
  }
  do_setclass(ch, (char *)"");
  return;
}

/* Display class information -Thoric */
void do_showclass(CHAR_DATA *ch, char *argument)
{
  char                    arg1[MIL], arg2[MIL];
  struct class_type      *Class;
  int                     cl, low, hi, ct, i;

  set_pager_color(AT_PLAIN, ch);
  argument = one_argument(argument, arg1);
  argument = one_argument(argument, arg2);
  if(!VLD_STR(arg1))
  {
    do_classes(ch, (char *)"");
    send_to_char("Syntax: showclass <class> [level range]\r\n", ch);
    return;
  }
  if(is_number(arg1) && (cl = atoi(arg1)) >= 0 && cl < MAX_CLASS)
    Class = class_table[cl];
  else
  {
    Class = NULL;
    for(cl = 0; cl < MAX_CLASS && class_table[cl]; cl++)
      if(!str_cmp(class_table[cl]->who_name, arg1))
      {
        Class = class_table[cl];
        break;
      }
  }
  if(!Class)
  {
    send_to_char("No such class.\r\n", ch);
    return;
  }
  pager_printf(ch, "&wCLASS: &W%s\r\n&w", VLD_STR(Class->who_name) ? Class->who_name : "(Not set)");
  pager_printf(ch, "&wFilename: &W%s\r\n&w", VLD_STR(Class->filename) ? Class->filename : "(Not set)");
  pager_printf(ch, "&wPrime Attribute:     &W%-14s\r\n", a_types[Class->attr_prime]);
  pager_printf(ch, "&wSecond Attribute:    &W%-14s\r\n", a_types[Class->attr_second]);
  pager_printf(ch, "&wDeficient Attribute: &W%-14s\r\n", a_types[Class->attr_deficient]);
  ct = 0;
  send_to_pager("&wDisallowed Races:&W\r\n", ch);
  for(i = 0; i < MAX_RACE; i++)
  {
    if(IS_SET(Class->race_restriction, 1 << i))
    {
      ct++;
      pager_printf(ch, "%10s ", race_table[i]->race_name);
      if(ct % 6 == 0)
        send_to_pager("\r\n", ch);
    }
  }
  if((ct % 6 != 0) || (ct == 0))
    send_to_pager("\r\n", ch);
  ct = 0;
  send_to_pager("&wAllowed Races:&W\r\n", ch);
  for(i = 0; i < MAX_RACE; i++)
  {
    if(!IS_SET(Class->race_restriction, 1 << i))
    {
      ct++;
      pager_printf(ch, "%10s ", race_table[i]->race_name);
      if(ct % 6 == 0)
        send_to_pager("\r\n", ch);
    }
  }

  send_to_char("\r\n", ch);
  pager_printf(ch, "&wMax Skill Adept: &W%-3d             &wThac0 : &W%-5d     &wThac32: &W%d\r\n", Class->skill_adept, Class->thac0_00, Class->thac0_32);
  pager_printf(ch,
               "&wHp Min/Hp Max  : &W%-2d/%-2d     &wMana Min/Max: &W%-2d/%-2d &wMove Min/Max:  &W%-2d/%-2d &wExpBase: &W%d\r\n",
               Class->hp_min, Class->hp_max, Class->mana_min, Class->mana_max, Class->move_min, Class->move_max, Class->exp_base);
  pager_printf(ch, "&W%s &wClass\r\n", Class->starting ? "Starting" : "Non-Starting");
  pager_printf(ch, "&wAffected by:  &W%s\r\n", !xIS_EMPTY(Class->affected) ? ext_flag_string(&Class->affected, a_flags) : "Nothing");
  pager_printf(ch, "&wResistant to: &W%s\r\n", flag_string(Class->resist, ris_flags));
  pager_printf(ch, "&wSusceptible to: &W%s\r\n", flag_string(Class->suscept, ris_flags));
  pager_printf(ch, "&wCrafting Base: &W%d\r\n", Class->craft_base);

  /*
   * These are no longer in use. -Taon if(Class->reclass1 > 0) pager_printf(ch, "%s ",
   * class_table[class->reclass1]->who_name); if(Class->reclass2 > 0) pager_printf(ch,
   * "%s ", class_table[class->reclass2]->who_name); if(Class->reclass3 > 0)
   * pager_printf(ch, "%s ", class_table[class->reclass3]->who_name);
   * send_to_pager("\r\n", ch); 
   */

  if(arg2 && arg2[0] != '\0')
  {
    int                     x, y, cnt;

    low = UMAX(0, atoi(arg2));
    hi = URANGE(low, atoi(argument), MAX_LEVEL);
    for(x = low; x <= hi; x++)
    {
      pager_printf(ch, "&wMale: &W%-30s &wFemale: &W%s\r\n", title_table[cl][x][0], title_table[cl][x][1]);
      cnt = 0;
      for(y = gsn_first_spell; y < gsn_top_sn; y++)
        if(skill_table[y]->skill_level[cl] == x)
        {
          pager_printf(ch, "  &[skill]%-7s %-19s%3d     ", skill_tname[skill_table[y]->type], skill_table[y]->name, skill_table[y]->skill_adept[cl]);
          if(++cnt % 2 == 0)
            send_to_pager("\r\n", ch);
        }
      if(cnt % 2 != 0)
        send_to_pager("\r\n", ch);
      send_to_pager("\r\n", ch);
    }
  }

  return;
}

void do_classes(CHAR_DATA *ch, char *argument)
{
  int                     iClass = 0;
  int                     counter = 0;

  if(!IS_IMMORTAL(ch))
    counter = 1;
  send_to_pager("\r\n", ch);
  for(iClass = 0; iClass < MAX_PC_CLASS; iClass++)
  {
    if(!class_table[iClass] || !VLD_STR(class_table[iClass]->who_name))
    {
      if(IS_IMMORTAL(ch))
        counter++;
      continue;
    }
    pager_printf(ch, "&c[&C%2d&c]&W %-15s", counter, class_table[iClass]->who_name);
    if(IS_IMMORTAL(ch))
      pager_printf(ch,
                   " &cStarting:&W%3s &cExpbase:&W %-5d &cHP: &W%3d-%-3d &cMana: &W%3d-%-3d &cMV: &W%3d-%-3d",
                   class_table[iClass]->starting ? "Yes" : "No",
                   class_table[iClass]->exp_base, class_table[iClass]->hp_min,
                   class_table[iClass]->hp_max, class_table[iClass]->mana_min, class_table[iClass]->mana_max, class_table[iClass]->move_min, class_table[iClass]->move_max);
    pager_printf(ch, "&D\r\n");
    counter++;
  }
  return;
}

bool load_class_file(const char *fname)
{
  char                    buf[MSL];
  const char             *word;
  bool                    fMatch;
  struct class_type      *Class;
  int                     cl = -1, tlev = 0;
  FILE                   *fp;

  snprintf(buf, MSL, "%s%s", CLASSDIR, fname);
  if((fp = FileOpen(buf, "r")) == NULL)
  {
    perror(buf);
    return FALSE;
  }
  CREATE(Class, struct class_type, 1);
  /*
   * Setup defaults for additions to class structure
   */
  Class->starting = FALSE;
  Class->attr_second = 0;
  Class->attr_deficient = 0;
  xCLEAR_BITS(Class->affected);
  Class->resist = 0;
  Class->suscept = 0;

  for(;;)
  {
    word = feof(fp) ? "End" : fread_word(fp);
    fMatch = FALSE;
    switch (UPPER(word[0]))
    {
      case '*':
        fMatch = TRUE;
        fread_to_eol(fp);
        break;
      case 'A':
        KEY("Affected", Class->affected, fread_bitvector(fp));
        KEY("AttrPrime", Class->attr_prime, fread_number(fp));
        KEY("AttrSecond", Class->attr_second, fread_number(fp));
        KEY("AttrDeficient", Class->attr_deficient, fread_number(fp));
        break;
      case 'C':
        KEY("Class", cl, fread_number(fp));
        KEY("Craftbase", Class->craft_base, fread_number(fp));
        break;
      case 'E':
        if(!str_cmp(word, "End"))
          if(!str_cmp(word, "End"))
          {
            FileClose(fp);
            if(cl < 0 || cl >= MAX_CLASS)
            {
              bug("Load_class_file: Class (%s) bad/not found (%d)", Class->who_name ? Class->who_name : "name not found", cl);
              if(VLD_STR(Class->who_name))
                STRFREE(Class->who_name);
              if(VLD_STR(Class->filename))
                STRFREE(Class->filename);
              DISPOSE(Class);
              return FALSE;
            }
            if(!Class->filename)
              Class->filename = STRALLOC(Class->who_name);
            class_table[cl] = Class;
            return TRUE;
          }
        KEY("ExpBase", Class->exp_base, fread_number(fp));
        break;
      case 'F':
        KEY("Filename", Class->filename, fread_string(fp));
      case 'H':
        KEY("HpMax", Class->hp_max, fread_number(fp));
        KEY("HpMin", Class->hp_min, fread_number(fp));
        break;
      case 'M':
        KEY("ManaMax", Class->mana_max, fread_number(fp));
        KEY("ManaMin", Class->mana_min, fread_number(fp));
        KEY("MoveMax", Class->move_max, fread_number(fp));
        KEY("MoveMin", Class->move_min, fread_number(fp));

        break;
      case 'N':
        KEY("Nocombo", Class->combo_restriction, fread_number(fp));
        KEY("Name", Class->who_name, fread_string(fp));
        break;
      case 'R':
        KEY("Races", Class->race_restriction, fread_number(fp));
        if(!str_cmp(word, "Reclass"))
        {
          Class->reclass1 = fread_number(fp);
          Class->reclass2 = fread_number(fp);
          Class->reclass3 = fread_number(fp);
          fMatch = TRUE;
          break;
        }
        KEY("Resist", Class->resist, fread_number(fp));
        break;
      case 'S':
        KEY("Starting", Class->starting, fread_number(fp));
        if(!str_cmp(word, "Skill"))
        {
          int                     sn, lev, adp;

          word = fread_word(fp);
          lev = fread_number(fp);
          adp = fread_number(fp);
          sn = skill_lookup(word);
          if(cl < 0 || cl >= MAX_CLASS)
            bug("load_class_file: Skill %s -- class bad/not found (%d)", word, cl);
          else if(!IS_VALID_SN(sn))
            bug("load_class_file: Skill %s unknown", word);
          else
          {
            skill_table[sn]->skill_level[cl] = lev;
            skill_table[sn]->skill_adept[cl] = adp;
          }
          fMatch = TRUE;
          break;
        }
        KEY("Skilladept", Class->skill_adept, fread_number(fp));
        KEY("Suscept", Class->suscept, fread_number(fp));
        break;
      case 'T':
        if(!str_cmp(word, "Title"))
        {
          if(cl < 0 || cl >= MAX_CLASS)
          {
            char                   *tmp;

            bug("load_class_file: Title -- class bad/not found (%d)", cl);
            tmp = fread_string(fp);
            DISPOSE(tmp);
            tmp = fread_string(fp);
            DISPOSE(tmp);
          }
          else if(tlev < MAX_LEVEL + 1)
          {
            title_table[cl][tlev][0] = fread_string(fp);
            title_table[cl][tlev][1] = fread_string(fp);
            ++tlev;
          }
          else
            bug("%s", "load_class_file: Too many titles");
          fMatch = TRUE;
          break;
        }
        KEY("Thac0", Class->thac0_00, fread_number(fp));
        KEY("Thac32", Class->thac0_32, fread_number(fp));
        break;
    }
    if(!fMatch)
    {
      bug("load_class_file: no match: %s", word);
      fread_to_eol(fp);
    }
  }
  return FALSE;
}

/* Load in all the class files. */
void load_classes()
{
  FILE                   *fpList;
  const char             *filename;
  char                    classlist[256];
  int                     i;

  MAX_PC_CLASS = 0;

  /*
   * Pre-init the class_table with blank classes
   */
  for(i = 0; i < MAX_CLASS; ++i)
    class_table[i] = NULL;

  snprintf(classlist, 256, "%s%s", CLASSDIR, CLASS_LIST);
  if((fpList = FileOpen(classlist, "r")) == NULL)
  {
    bug("%s: Can't open %s for reading", __FUNCTION__, CLASS_LIST);
    perror(classlist);
    exit(1);
  }

  for(;;)
  {
    filename = feof(fpList) ? "$" : fread_word(fpList);
    if(filename[0] == '$')
      break;

    if(!load_class_file(filename))
    {
      bug("%s Cannot load class file: %s", __FUNCTION__, filename);
    }
    else
      ++MAX_PC_CLASS;
  }
  FileClose(fpList);
  for(i = 0; i < MAX_CLASS; ++i)
  {
    if(class_table[i] == NULL)
    {
      CREATE(class_table[i], struct class_type, 1);

      create_new_class(i, (char *)"");
    }
  }
  return;
}

void write_class_file(int cl)
{
  FILE                   *fpout;
  char                    filename[MIL];
  struct class_type      *Class = class_table[cl];
  int                     x, y;

  snprintf(filename, MIL, "%s%s.class", CLASSDIR, Class->filename);
  if((fpout = FileOpen(filename, "w")) == NULL)
  {
    bug("Cannot open: %s for writing", filename);
    return;
  }
  if(VLD_STR(Class->who_name))
    fprintf(fpout, "Name           %s~\n", Class->who_name);
  if(VLD_STR(Class->filename))
    fprintf(fpout, "Filename       %s~\n", Class->filename);
  fprintf(fpout, "Class          %d\n", cl);
  fprintf(fpout, "Starting       %d\n", Class->starting);
  fprintf(fpout, "AttrPrime      %d\n", Class->attr_prime);
  fprintf(fpout, "AttrSecond     %d\n", Class->attr_second);
  fprintf(fpout, "AttrDeficient  %d\n", Class->attr_deficient);
  fprintf(fpout, "Races          %d\n", Class->race_restriction);
  fprintf(fpout, "Nocombo        %d\n", Class->combo_restriction);
  fprintf(fpout, "Reclass        %d %d %d\n", Class->reclass1, Class->reclass2, Class->reclass3);
  fprintf(fpout, "Skilladept     %d\n", Class->skill_adept);
  fprintf(fpout, "Thac0          %d\n", Class->thac0_00);
  fprintf(fpout, "Thac32         %d\n", Class->thac0_32);
  fprintf(fpout, "Hpmin          %d\n", Class->hp_min);
  fprintf(fpout, "Hpmax          %d\n", Class->hp_max);
  fprintf(fpout, "ManaMin        %d\n", Class->mana_min);
  fprintf(fpout, "ManaMax        %d\n", Class->mana_max);
  fprintf(fpout, "MoveMin        %d\n", Class->move_min);
  fprintf(fpout, "MoveMax        %d\n", Class->move_max);

  fprintf(fpout, "Expbase        %d\n", Class->exp_base);
  fprintf(fpout, "Craftbase        %d\n", Class->craft_base);
  if(!xIS_EMPTY(Class->affected))
    fprintf(fpout, "Affected       %s\n", print_bitvector(&Class->affected));
  fprintf(fpout, "Resist         %d\n", Class->resist);
  fprintf(fpout, "Suscept        %d\n", Class->suscept);
  for(x = 0; x < top_sn; x++)
  {
    if(!skill_table[x]->name || skill_table[x]->name[0] == '\0')
      break;
    if((y = skill_table[x]->skill_level[cl]) < LEVEL_IMMORTAL)
      fprintf(fpout, "Skill '%s' %d %d\n", skill_table[x]->name, y, skill_table[x]->skill_adept[cl]);
  }
  for(x = 0; x <= MAX_LEVEL; x++)
    fprintf(fpout, "Title\n%s~\n%s~\n", title_table[cl][x][0], title_table[cl][x][1]);
  fprintf(fpout, "End\n");
  FileClose(fpout);
}

void save_classes()
{
  int                     x;

  for(x = 0; x < MAX_PC_CLASS; x++)
    write_class_file(x);
}

void do_trades(CHAR_DATA *ch, char *argument)
{
  send_to_char("&c[&C20&c]&W Blacksmith\r\n&c[&C21&c]&W Baker\r\n&c[&C22&c]&W Tanner\r\n&c[&C24&c]&W Jeweler\r\n&c[&C25&c]&W Carpenter\r\n&c[&C26]&W Dragonic", ch);
  send_to_char("\r\nTo change a tradeclass, use mset victim tradeclass number.\r\nSee also help classes, and help races.\r\n", ch);

  return;
}