dbna/clans/
dbna/councils/
dbna/deity/
dbna/gods/
dbna/houses/
dbna/space/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 *------------------------------------------------------------------------  *
 *                      Shaddai's Polymorph                                 *
 ****************************************************************************/

#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "mud.h"

#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )                                    \
                                if ( !str_cmp( word, literal ) )        \
                                {                                       \
                                    field  = value;                     \
                                    fMatch = TRUE;                      \
                                    break;                              \
                                }

MORPH_DATA *morph_start = NULL;
MORPH_DATA *morph_end = NULL;
int morph_vnum = 0;

/*
 * Local functions
 */
void copy_morph args( ( MORPH_DATA * morph, MORPH_DATA * temp ) );

/*
 * Extern prototypes
 */
int get_risflag args( ( char *flag ) );
int get_npc_race args( ( char *type ) );
int get_pc_race args( ( char *type ) );

/*
 *  Command used to set all the morphing information.
 *  Must use the morphset save command, to write the commands to file.
 *  Blood/Hp/Mana/Move/Hitroll/Damroll can be set using variables such
 *  as 1d2+10.  No boundry checks are in place yet on those, so care must
 *  be taken when using these.  --Shaddai
 */

void do_morphset( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char arg3[MAX_INPUT_LENGTH];
  char buf[MAX_STRING_LENGTH];
  char *origarg = argument;
  int value;
  MORPH_DATA *morph = NULL;

  set_char_color( AT_PLAIN, ch );

  if( IS_NPC( ch ) )
  {
    send_to_char( "Mob's can't morphset\n\r", ch );
    return;
  }

  if( !ch->desc )
  {
    send_to_char( "You have no descriptor\n\r", ch );
    return;
  }

  switch ( ch->substate )
  {
    default:
      break;
    case SUB_MORPH_DESC:
      if( !ch->dest_buf )
      {
        send_to_char( "Fatal error: report to Shaddai.\n\r", ch );
        bug( "do_morphset: sub_morph_desc: NULL ch->dest_buf", 0 );
        ch->substate = SUB_NONE;
        return;
      }
      morph = ch->dest_buf;
      STRFREE( morph->description );
      morph->description = copy_buffer( ch );
      stop_editing( ch );
      ch->substate = ch->tempnum;
      if( ch->substate == SUB_REPEATCMD )
        ch->dest_buf = morph;
      return;
    case SUB_MORPH_HELP:
      if( !ch->dest_buf )
      {
        send_to_char( "Fatal error: report to Shaddai.\n\r", ch );
        bug( "do_morphset: sub_morph_help: NULL ch->dest_buf", 0 );
        ch->substate = SUB_NONE;
        return;
      }
      morph = ch->dest_buf;
      STRFREE( morph->help );
      morph->help = copy_buffer( ch );
      stop_editing( ch );
      ch->substate = ch->tempnum;
      if( ch->substate == SUB_REPEATCMD )
        ch->dest_buf = morph;
      return;
  }

  morph = NULL;
  smash_tilde( argument );

  if( ch->substate == SUB_REPEATCMD )
  {
    morph = ch->dest_buf;
    if( !morph )
    {
      send_to_char( "Someone deleted your morph!\n\r", ch );
      argument = "done";
    }
    if( argument[0] == '\0' || !str_cmp( argument, " " ) )
    {
      do_morphstat( ch, morph->name );
      return;
    }
    if( !str_cmp( argument, "stat" ) )
    {
      strcpy( buf, morph->name );
      strcat( buf, " help" );
      do_morphstat( ch, buf );
      return;
    }
    if( !str_cmp( argument, "done" ) || !str_cmp( argument, "off" ) )
    {
      send_to_char( "Morphset mode off.\n\r", ch );
      ch->substate = SUB_NONE;
      ch->dest_buf = NULL;
      if( ch->pcdata && ch->pcdata->subprompt )
      {
        STRFREE( ch->pcdata->subprompt );
        ch->pcdata->subprompt = NULL;
      }
      return;
    }
  }
  if( morph )
  {
    strcpy( arg1, morph->name );
    argument = one_argument( argument, arg2 );
    strcpy( arg3, argument );
  }
  else
  {
    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );
    strcpy( arg3, argument );
  }
  if( !str_cmp( arg1, "on" ) )
  {
    send_to_char( "Syntax: morphset <morph> on.\n\r", ch );
    return;
  }
  value = is_number( arg3 ) ? atoi( arg3 ) : -1;

  if( atoi( arg3 ) < -1 && value == -1 )
    value = atoi( arg3 );

  if( ch->substate != SUB_REPEATCMD && arg1[0] != '\0' && !str_cmp( arg1, "save" ) )
  {
    save_morphs(  );
    send_to_char( "Morph data saved.\n\r", ch );
    return;
  }
  if( arg1[0] == '\0' || ( arg2[0] == '\0' && ch->substate != SUB_REPEATCMD ) || !str_cmp( arg1, "?" ) )
  {
    if( ch->substate == SUB_REPEATCMD )
    {
      if( morph )
        send_to_char( "Syntax: <field>  <value>\n\r", ch );
      else
        send_to_char( "Syntax: <morph> <field>  <value>\n\r", ch );
    }
    else
      send_to_char( "Syntax: morphset <morph> <field>  <value>\n\r", ch );
    send_to_char( "Syntax: morphset save\n\r", ch );
    send_to_char( "\n\r", ch );
    send_to_char_color( "&cField being one of:\n\r", ch );
    send_to_char_color( "&c-------------------------------------------------\n\r", ch );
    send_to_char( "  ac, affected, blood, bloodused, cha, class, con, damroll, dayto,\n\r", ch );
    send_to_char( "  dayfrom, deity, description, defpos, dex, dodge,\n\r", ch );
    send_to_char( "  favourused, gloryused, help, hitroll, hp, hpused, immune,\n\r", ch );
    send_to_char( "  int, str, keyword, lck, level, long, mana, manaused,\n\r", ch );
    send_to_char( "  morphother, morphself, move, moveused, name, noaffected,\n\r", ch );
    send_to_char( "  nocast, noimmune, noresistant, noskill, nosusceptible,\n\r", ch );
    send_to_char( "  obj1, obj2, obj3, objuse1, objuse2, objuse3, parry,\n\r", ch );
    send_to_char( "  pkill, race, resistant, sav1, sav2, sav3, sav4, sav5,\n\r", ch );
    send_to_char( "  sex, short, skills, susceptible, timefrom, timer, timeto,\n\r", ch );
    send_to_char( "  tumble, unmorphother, unmorphself, wis.\n\r", ch );
    send_to_char_color( "&c-------------------------------------------------\n\r", ch );
    return;
  }

  if( !morph )
  {

    if( !is_number( arg1 ) )
      morph = get_morph( arg1 );
    else
      morph = get_morph_vnum( atoi( arg1 ) );

    if( morph == NULL )
    {
      send_to_char( "That morph does not exist.\n\r", ch );
      return;
    }
  }
  if( !str_cmp( arg2, "on" ) )
  {
    CHECK_SUBRESTRICTED( ch );
    ch_printf( ch, "Morphset mode on. (Editing %s).\n\r", morph->name );
    ch->substate = SUB_REPEATCMD;
    ch->dest_buf = morph;
    if( ch->pcdata )
    {
      if( ch->pcdata->subprompt )
      {
        STRFREE( ch->pcdata->subprompt );
        ch->pcdata->subprompt = NULL;
      }
      sprintf( buf, "<&CMorphset &W%s&w> %%i", morph->name );
      ch->pcdata->subprompt = STRALLOC( buf );
    }
    return;
  }
  if( !str_cmp( arg2, "str" ) )
  {
    if( value < -10 || value > 10 )
    {
      send_to_char( "Strength must be a value from -10 to 10.\n\r", ch );
      return;
    }
    morph->str = value;
  }
  else if( !str_cmp( arg2, "int" ) )
  {
    if( value < -10 || value > 10 )
    {
      send_to_char( "Intelligence must be a value from -10 to 10.\n\r", ch );
      return;
    }
    morph->inte = value;
  }
  else if( !str_cmp( arg2, "defpos" ) )
  {
    if( value < 0 || value > POS_STANDING )
    {
      ch_printf( ch, "Position range is 0 to %d.\n\r", POS_STANDING );
      return;
    }
    morph->defpos = value;
  }
  else if( !str_cmp( arg2, "dex" ) )
  {
    if( value < -10 || value > 10 )
    {
      send_to_char( "Dexterity must be a value from -10 to 10.\n\r", ch );
      return;
    }
    morph->dex = value;
  }
  else if( !str_cmp( arg2, "con" ) )
  {
    if( value < -10 || value > 10 )
    {
      send_to_char( "Constitution must be a value from -10 to 10.\n\r", ch );
      return;
    }
    morph->con = value;
  }
  else if( !str_cmp( arg2, "lck" ) )
  {
    if( value < -10 || value > 10 )
    {
      send_to_char( "Luck must be a value from -10 to 10.\n\r", ch );
      return;
    }
    morph->lck = value;
  }
  else if( !str_cmp( arg2, "sex" ) )
  {
    if( ( value < 0 || value > 2 ) && value != -1 )
    {
      send_to_char( "Sex must be a value from 0 to 2.\n\r", ch );
      return;
    }
    morph->sex = value;
  }
  else if( !str_cmp( arg2, "pkill" ) )
  {
    if( !str_cmp( arg3, "pkill" ) )
      morph->pkill = ONLY_PKILL;
    else if( !str_cmp( arg3, "peace" ) )
      morph->pkill = ONLY_PEACEFULL;
    else if( !str_cmp( arg3, "none" ) )
      morph->pkill = 0;
    else
    {
      send_to_char( "Usuage: morphset <morph> pkill [pkill|peace|none]\n\r", ch );
      return;
    }
  }
  else if( !str_cmp( arg2, "bloodused" ) )
  {
    if( value < 0 || value > 60 )
    {
      send_to_char( "Blood used is a value from 0 to 60.\n\r", ch );
      return;
    }
    morph->bloodused = value;
  }
  else if( !str_cmp( arg2, "manaused" ) )
  {
    if( value < 0 || value > 2000 )
    {
      send_to_char( "Mana used is a value from 0 to 2000.\n\r", ch );
      return;
    }
    morph->manaused = value;
  }
  else if( !str_cmp( arg2, "moveused" ) )
  {
    if( value < 0 || value > 2000 )
    {
      send_to_char( "Move used is a value from 0 to 2000.\n\r", ch );
      return;
    }
    morph->moveused = value;
  }
  else if( !str_cmp( arg2, "hpused" ) )
  {
    if( value < 0 || value > 2000 )
    {
      send_to_char( "Hp used is a value from 0 to 2000.\n\r", ch );
      return;
    }
    morph->hpused = value;
  }
  else if( !str_cmp( arg2, "favourused" ) )
  {
    if( value < 0 || value > 2000 )
    {
      send_to_char( "Favour used is a value from 0 to 2000.\n\r", ch );
      return;
    }
    morph->favourused = value;
  }
  else if( !str_cmp( arg2, "gloryused" ) )
  {
    if( value < 0 || value > 2000 )
    {
      send_to_char( "Glory used is a value from 0 to 2000.\n\r", ch );
      return;
    }
    morph->gloryused = value;
  }
  else if( !str_cmp( arg2, "timeto" ) )
  {
    if( value < 0 || value > 23 )
    {
      send_to_char( "Timeto is a value from 0 to 23.\n\r", ch );
      return;
    }
    morph->timeto = value;
  }
  else if( !str_cmp( arg2, "timefrom" ) )
  {
    if( value < 0 || value > 23 )
    {
      send_to_char( "Timefrom is a value from 0 to 23.\n\r", ch );
      return;
    }
    morph->timefrom = value;
  }
  else if( !str_cmp( arg2, "dayto" ) )
  {
    if( value < 0 || value > 31 )
    {
      send_to_char( "Dayto is a value from 0 to 31.\n\r", ch );
      return;
    }
    morph->dayto = value;
  }
  else if( !str_cmp( arg2, "dayfrom" ) )
  {
    if( value < 0 || value > 31 )
    {
      send_to_char( "Dayfrom is a value from 0 to 31.\n\r", ch );
      return;
    }
    morph->dayfrom = value;
  }
  else if( !str_cmp( arg2, "sav1" ) || !str_cmp( arg2, "savepoison" ) )
  {
    if( value < -30 || value > 30 )
    {
      send_to_char( "Saving throw range is -30 to 30.\n\r", ch );
      return;
    }
    morph->saving_poison_death = value;
  }
  else if( !str_cmp( arg2, "sav2" ) || !str_cmp( arg2, "savewand" ) )
  {
    if( value < -30 || value > 30 )
    {
      send_to_char( "Saving throw range is -30 to 30.\n\r", ch );
      return;
    }
    morph->saving_wand = value;
  }
  else if( !str_cmp( arg2, "sav3" ) || !str_cmp( arg2, "savepara" ) )
  {
    if( value < -30 || value > 30 )
    {
      send_to_char( "Saving throw range is -30 to 30.\n\r", ch );
      return;
    }
    morph->saving_para_petri = value;
  }
  else if( !str_cmp( arg2, "sav4" ) || !str_cmp( arg2, "savebreath" ) )
  {
    if( value < -30 || value > 30 )
    {
      send_to_char( "Saving throw range is -30 to 30.\n\r", ch );
      return;
    }
    morph->saving_breath = value;
  }
  else if( !str_cmp( arg2, "sav5" ) || !str_cmp( arg2, "savestaff" ) )
  {
    if( value < -30 || value > 30 )
    {
      send_to_char( "Saving throw range is -30 to 30.\n\r", ch );
      return;
    }
    morph->saving_spell_staff = value;
  }
  else if( !str_cmp( arg2, "timer" ) )
  {
    if( value < -1 || value == 0 )
    {
      send_to_char( "Timer must be -1 (None) or greater than 0.\n\r", ch );
      return;
    }
    morph->timer = value;
  }
  else if( !str_cmp( arg2, "hp" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->hit = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "mana" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->mana = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "move" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->move = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "blood" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->blood = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "ac" ) )
  {
    if( value > 500 || value < -500 )
    {
      send_to_char( "Ac range is -500 to 500.\n\r", ch );
      return;
    }
    morph->ac = value;
  }
  else if( !str_cmp( arg2, "hitroll" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->hitroll = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "damroll" ) )
  {
    argument = one_argument( argument, arg3 );
    if( !str_cmp( arg3, "0" ) )
      arg3[0] = '\0';
    morph->damroll = str_dup( arg3 );
  }
  else if( !str_cmp( arg2, "dodge" ) )
  {
    if( value > 100 || value < -100 )
    {
      send_to_char( "Dodge range is -100 to 100.\n\r", ch );
      return;
    }
    morph->dodge = value;
  }
  else if( !str_prefix( "obj", arg2 ) )
  {
    int index;
    char temp[MAX_STRING_LENGTH];

    if( arg2[3] == '\0' )
    {
      send_to_char( "Obj 1, 2, or 3.\n\r", ch );
      return;
    }
    temp[0] = arg2[3];
    temp[1] = '\0';
    index = atoi( temp );
    if( index > 3 || index < 1 )
    {
      send_to_char( "Obj 1, 2, or 3.\n\r", ch );
      return;
    }
    if( ( get_obj_index( value ) ) == NULL )
    {
      send_to_char( "No such vnum.\n\r", ch );
      return;
    }
    morph->obj[index - 1] = value;
  }
  else if( !str_cmp( arg2, "parry" ) )
  {
    if( value > 100 || value < -100 )
    {
      send_to_char( "Dodge range is -100 to 100.\n\r", ch );
      return;
    }
    morph->parry = value;
  }
  else if( !str_cmp( arg2, "tumble" ) )
  {
    if( value > 100 || value < -100 )
    {
      send_to_char( "Dodge range is -100 to 100.\n\r", ch );
      return;
    }
    morph->tumble = value;
  }
  else if( !str_cmp( arg2, "level" ) )
  {
    if( value < 0 || value > 51 )
    {
      send_to_char( "Level range is 0 to 51.\n\r", ch );
      return;
    }
    morph->level = value;
  }
  else if( !str_prefix( arg2, "objuse" ) )
  {
    int index;
    char temp[MAX_INPUT_LENGTH];

    if( arg2[6] == '\0' )
    {
      send_to_char( "Objuse 1, 2 or 3?\n\r", ch );
      return;
    }
    temp[0] = arg2[6];
    temp[1] = '\0';
    index = atoi( temp );
    if( index > 3 || index < 1 )
    {
      send_to_char( "Objuse 1, 2, or 3?\n\r", ch );
      return;
    }
    if( value )
      morph->objuse[index - 1] = TRUE;
    else
      morph->objuse[index - 1] = FALSE;
  }
  else if( !str_cmp( arg2, "nocast" ) )
  {
    if( value )
      morph->no_cast = TRUE;
    else
      morph->no_cast = FALSE;
  }
  else if( !str_cmp( arg2, "resistant" ) || !str_cmp( arg2, "r" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> resistant <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->resistant, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "susceptible" ) || !str_cmp( arg2, "s" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> susceptible <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->suscept, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "immune" ) || !str_cmp( arg2, "i" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> immune <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->immune, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "noresistant" ) || !str_cmp( arg2, "nr" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> noresistant <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->no_resistant, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "nosusceptible" ) || !str_cmp( arg2, "ns" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> nosusceptible <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->no_suscept, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "noimmune" ) || !str_cmp( arg2, "ni" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> noimmune <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_risflag( arg3 );
      if( value < 0 || value > 31 )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        TOGGLE_BIT( morph->no_immune, 1 << value );
    }
  }
  else if( !str_cmp( arg2, "affected" ) || !str_cmp( arg2, "aff" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> affected <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_aflag( arg3 );
      if( value < 0 || value > MAX_BITS )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        xTOGGLE_BIT( morph->affected_by, value );
    }
  }
  else if( !str_cmp( arg2, "noaffected" ) || !str_cmp( arg2, "naff" ) )
  {
    if( !argument || argument[0] == '\0' )
    {
      send_to_char( "Usage: morphset <morph> noaffected <flag> [flag]...\n\r", ch );
      return;
    }
    while( argument[0] != '\0' )
    {
      argument = one_argument( argument, arg3 );
      value = get_aflag( arg3 );
      if( value < 0 || value > MAX_BITS )
        ch_printf( ch, "Unknown flag: %s\n\r", arg3 );
      else
        xTOGGLE_BIT( morph->no_affected_by, value );
    }
  }
  else if( !str_cmp( arg2, "short" ) )
  {
    STRFREE( morph->short_desc );
    morph->short_desc = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "morphother" ) )
  {
    STRFREE( morph->morph_other );
    morph->morph_other = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "morphself" ) )
  {
    STRFREE( morph->morph_self );
    morph->morph_self = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "unmorphother" ) )
  {
    STRFREE( morph->unmorph_other );
    morph->unmorph_other = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "unmorphself" ) )
  {
    STRFREE( morph->unmorph_self );
    morph->unmorph_self = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "keyword" ) )
  {
    STRFREE( morph->key_words );
    morph->key_words = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "long" ) )
  {
    STRFREE( morph->long_desc );
    strcpy( buf, arg3 );
    strcat( buf, "\n\r" );
    morph->long_desc = STRALLOC( buf );
  }
  else if( !str_cmp( arg2, "description" ) || !str_cmp( arg2, "desc" ) )
  {
    if( arg3[0] )
    {
      STRFREE( morph->description );
      morph->description = STRALLOC( arg3 );
    }
    CHECK_SUBRESTRICTED( ch );
    if( ch->substate == SUB_REPEATCMD )
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_MORPH_DESC;
    ch->dest_buf = morph;
    start_editing( ch, morph->description );
    return;
  }
  else if( !str_cmp( arg2, "name" ) )
  {
    STRFREE( morph->name );
    morph->name = STRALLOC( arg3 );
  }
  else if( !str_cmp( arg2, "help" ) )
  {
    if( arg3[0] )
    {
      STRFREE( morph->help );
      morph->help = STRALLOC( arg3 );
    }
    CHECK_SUBRESTRICTED( ch );
    if( ch->substate == SUB_REPEATCMD )
      ch->tempnum = SUB_REPEATCMD;
    else
      ch->tempnum = SUB_NONE;
    ch->substate = SUB_MORPH_HELP;
    ch->dest_buf = morph;
    start_editing( ch, morph->help );
    return;
  }
  else if( !str_cmp( arg2, "skills" ) )
  {
    if( !morph->skills )
      morph->skills = str_dup( "" );

    if( arg3[0] == '\0' || !str_cmp( arg3, "none" ) )
    {
      DISPOSE( morph->skills );
      morph->skills = str_dup( "" );
      return;
    }
    sprintf( buf, "%s %s", morph->skills, arg3 );
    DISPOSE( morph->skills );
    morph->skills = str_dup( buf );
  }
  else if( !str_cmp( arg2, "noskills" ) )
  {
    if( !morph->no_skills )
      morph->no_skills = str_dup( "" );

    if( arg3[0] == '\0' || !str_cmp( arg3, "none" ) )
    {
      DISPOSE( morph->no_skills );
      morph->no_skills = str_dup( "" );
      return;
    }
    sprintf( buf, "%s %s", morph->no_skills, arg3 );
    DISPOSE( morph->no_skills );
    morph->no_skills = str_dup( buf );
  }
  else if( !str_cmp( arg2, "class" ) )
  {
    if( !is_number( arg3 ) )
    {
      int i;
      for( i = 0; i < MAX_PC_CLASS; i++ )
        if( !str_cmp( class_table[i]->who_name, arg3 ) )
          break;
      value = i;
    }
    if( value < 0 || value >= MAX_PC_CLASS )
    {
      ch_printf( ch, "Class range is 0 to %d.\n", MAX_PC_CLASS );
      return;
    }
    TOGGLE_BIT( morph->class, ( 1 << value ) );
  }
  else if( !str_cmp( arg2, "race" ) )
  {
    if( is_number( arg3 ) )
      value = atoi( arg3 );
    else
      value = get_pc_race( arg3 );
    if( value < 0 && !is_number( arg3 ) )
    {
      ch_printf( ch, "Unknown race: %s", arg3 );
      return;
    }
    else if( value < 0 || value >= MAX_PC_RACE )
    {
      ch_printf( ch, "Race range is 0 to %d.\n", MAX_PC_RACE - 1 );
      return;
    }
    TOGGLE_BIT( morph->race, ( 1 << value ) );
  }
  else if( ch->substate == SUB_REPEATCMD )
  {
    ch->substate = SUB_RESTRICTED;
    interpret( ch, origarg );
    ch->substate = SUB_REPEATCMD;
    ch->last_cmd = do_morphset;
  }
  else
  {
    do_morphset( ch, "" );
    return;
  }
  send_to_char( "Done.\n\r", ch );
  return;
}

/*
 *  Shows morph info on a given morph.
 *  To see the description and help file, must use morphstat <morph> help
 *  Shaddai
 */

void do_morphstat( CHAR_DATA * ch, char *argument )
{
  MORPH_DATA *morph;
  char arg[MAX_INPUT_LENGTH];
  int count = 1;

  set_pager_color( AT_CYAN, ch );

  argument = one_argument( argument, arg );
  if( arg[0] == '\0' )
  {
    send_to_pager( "Morphstat what?\n\r", ch );
    return;
  }
  if( IS_NPC( ch ) )
  {
    send_to_char( "Mob's can't morphstat\n\r", ch );
    return;
  }

  if( !str_cmp( arg, "list" ) )
  {
    if( !morph_start )
    {
      send_to_pager( "No morph's currently exist.\n\r", ch );
      return;
    }
    for( morph = morph_start; morph; morph = morph->next )
    {
      pager_printf_color( ch, "&c[&C%2d&c]   Name:  &C%-13s    &cVnum:  &C%4d  &cUsed:  &C%3d\n\r",
                          count, morph->name, morph->vnum, morph->used );
      count++;
    }
    return;
  }
  if( !is_number( arg ) )
    morph = get_morph( arg );
  else
    morph = get_morph_vnum( atoi( arg ) );

  if( morph == NULL )
  {
    send_to_pager( "No such morph exists.\n\r", ch );
    return;
  }
  if( argument[0] == '\0' )
  {
    pager_printf_color( ch, "  &cMorph Name: &C%-20s  Vnum: %4d\n\r", morph->name, morph->vnum );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    send_to_pager_color( "                           &BMorph Restrictions\n\r", ch );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch, "  &cClasses Allowed   : &w%s\n\r", class_string( morph->class ) );
    pager_printf_color( ch, "  &cRaces Not Allowed: &w%s\n\r", race_string( morph->race ) );
    pager_printf_color( ch, "  &cSex:  &C%s   &cPkill:   &C%s   &cTime From:   &C%d   &cTime To:    &C%d\n\r",
                        morph->sex == SEX_MALE ? "male" :
                        morph->sex == SEX_FEMALE ? "female" : "neutral",
                        morph->pkill == ONLY_PKILL ? "YES" :
                        morph->pkill == ONLY_PEACEFULL ? "NO" : "n/a", morph->timefrom, morph->timeto );
    pager_printf_color( ch, "  &cDay From:  &C%d  &cDay To:  &C%d\n\r", morph->dayfrom, morph->dayto );
    pager_printf_color( ch, "  &cLevel:  &C%d       &cCasting Allowed   : &C%s\n\r",
                        morph->level, ( morph->no_cast ) ? "NO" : "yes" );
    pager_printf_color( ch, "  &cUSAGES:  Mana:  &C%d  &cMove:  &C%d  &cHp:  &C%d  &cFavour:  &C%d  &cGlory:  &C%d\n\r",
                        morph->manaused, morph->moveused, morph->hpused, morph->favourused, morph->gloryused );
    pager_printf_color( ch, "           &cBlood:  &C%d\n\r", morph->bloodused );
    pager_printf_color( ch,
                        "  &cObj1: &C%d  &cObjuse1: &C%s   &cObj2: &C%d  &cObjuse2: &C%s   &cObj3: &C%d  &cObjuse3: &c%s\n\r",
                        morph->obj[0], ( morph->objuse[0] ? "YES" : "no" ), morph->obj[1],
                        ( morph->objuse[1] ? "YES" : "no" ), morph->obj[2], ( morph->objuse[2] ? "YES" : "no" ) );
    pager_printf_color( ch, "  &cTimer: &w%d\n\r", morph->timer );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    send_to_pager_color( "                       &BEnhancements to the Player\n\r", ch );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch,
                        "  &cStr: &C%2d&c )( Int: &C%2d&c )( Wis: &C0&c )( Dex: &C%2d&c )( Con: &C%2d&c )( Cha: &C0&c )( Lck: &C%2d&c )\n\r",
                        morph->str, morph->inte, morph->dex, morph->con, morph->lck );
    pager_printf_color( ch, "  &cSave versus: &w%d %d %d %d %d       &cDodge: &w%d  &cParry: &w%d  &cTumble: &w%d\n\r",
                        morph->saving_poison_death, morph->saving_wand, morph->saving_para_petri, morph->saving_breath,
                        morph->saving_spell_staff, morph->dodge, morph->parry, morph->tumble );
    pager_printf_color( ch, "  &cHps     : &w%s    &cBlood  : &w%s    &cMana   : &w%s    &cMove      : &w%s\n\r", morph->hit,
                        morph->blood, morph->mana, morph->move );
    pager_printf_color( ch, "  &cDamroll : &w%s    &cHitroll: &w%s    &cAC     : &w%d\n\r", morph->damroll, morph->hitroll,
                        morph->ac );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    send_to_pager_color( "                          &BAffects to the Player\n\r", ch );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch, "  &cAffected by: &C%s\n\r", affect_bit_name( &morph->affected_by ) );
    pager_printf_color( ch, "  &cImmune     : &w%s\n\r", flag_string( morph->immune, ris_flags ) );
    pager_printf_color( ch, "  &cSusceptible: &w%s\n\r", flag_string( morph->suscept, ris_flags ) );
    pager_printf_color( ch, "  &cResistant  : &w%s\n\r", flag_string( morph->resistant, ris_flags ) );
    pager_printf_color( ch, "  &cSkills     : &w%s\n\r", morph->skills );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    send_to_pager_color( "                     &BPrevented affects to the Player\n\r", ch );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch, "  &cNot affected by: &C%s\n\r", affect_bit_name( &morph->no_affected_by ) );
    pager_printf_color( ch, "  &cNot Immune     : &w%s\n\r", flag_string( morph->no_immune, ris_flags ) );
    pager_printf_color( ch, "  &cNot Susceptible: &w%s\n\r", flag_string( morph->no_suscept, ris_flags ) );
    pager_printf_color( ch, "  &cNot Resistant  : &w%s\n\r", flag_string( morph->no_resistant, ris_flags ) );
    pager_printf_color( ch, "  &cNot Skills     : &w%s\n\r", morph->no_skills );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    send_to_char( "\n\r", ch );
  }
  else if( !str_cmp( argument, "help" ) || !str_cmp( argument, "desc" ) )
  {
    pager_printf_color( ch, "  &cMorph Name  : &C%-20s\n\r", morph->name );
    pager_printf_color( ch, "  &cDefault Pos : &w%d\n\r", morph->defpos );
    pager_printf_color( ch, "  &cKeywords    : &w%s\n\r", morph->key_words );
    pager_printf_color( ch, "  &cShortdesc   : &w%s\n\r",
                        ( morph->short_desc[0] == '\0' ) ? "(none set)" : morph->short_desc );
    pager_printf_color( ch, "  &cLongdesc    : &w%s",
                        ( morph->long_desc[0] == '\0' ) ? "(none set)\n\r" : morph->long_desc );
    pager_printf_color( ch, "  &cMorphself   : &w%s\n\r", morph->morph_self );
    pager_printf_color( ch, "  &cMorphother  : &w%s\n\r", morph->morph_other );
    pager_printf_color( ch, "  &cUnMorphself : &w%s\n\r", morph->unmorph_self );
    pager_printf_color( ch, "  &cUnMorphother: &w%s\n\r", morph->unmorph_other );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch, "                                  &cHelp:\n\r&w%s\n\r", morph->help );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
    pager_printf_color( ch, "                               &cDescription:\n\r&w%s\n\r", morph->description );
    send_to_pager_color( "&B[----------------------------------------------------------------------------]\n\r", ch );
  }
  else
  {
    send_to_char( "Syntax: morphstat <morph>\n\r", ch );
    send_to_char( "Syntax: morphstat <morph> <help/desc>\n\r", ch );
  }
  return;
}

char *class_string( int bitvector )
{
  static char buf[MAX_STRING_LENGTH];
  int x;

  buf[0] = '\0';
  for( x = 0; x < MAX_PC_CLASS; x++ )
    if( IS_SET( bitvector, 1 << x ) )
    {
      strcat( buf, class_table[x]->who_name );
      strcat( buf, " " );
    }
  if( ( x = strlen( buf ) ) > 0 )
    buf[--x] = '\0';

  return buf;
}

char *race_string( int bitvector )
{
  static char buf[MAX_STRING_LENGTH];
  int x;

  buf[0] = '\0';
  for( x = 0; x < MAX_PC_RACE; x++ )
    if( IS_SET( bitvector, 1 << x ) )
    {
      strcat( buf, race_table[x]->race_name );
      strcat( buf, " " );
    }
  if( ( x = strlen( buf ) ) > 0 )
    buf[--x] = '\0';

  return buf;
}

/*
 * Given the Morph's name, returns the pointer to the morph structure.
 * --Shaddai
 */

MORPH_DATA *get_morph( char *arg )
{
  MORPH_DATA *morph;

  if( arg[0] == '\0' )
    return NULL;
  for( morph = morph_start; morph; morph = morph->next )
    if( !str_cmp( morph->name, arg ) )
      break;
  return morph;
}

/*
 * Find a morph you can use -- Shaddai
 */
MORPH_DATA *find_morph( CHAR_DATA * ch, char *target, bool is_cast )
{
  MORPH_DATA *morph;
  if( target[0] == '\0' )
    return NULL;
  for( morph = morph_start; morph; morph = morph->next )
  {
    if( str_cmp( morph->name, target ) )
      continue;
    if( can_morph( ch, morph, is_cast ) )
      break;
  }
  return morph;
}


/*
 * Given the Morph's vnum, returns the pointer to the morph structure.
 * --Shaddai
 */

MORPH_DATA *get_morph_vnum( int vnum )
{
  MORPH_DATA *morph;

  if( vnum < 1 )
    return NULL;
  for( morph = morph_start; morph; morph = morph->next )
    if( morph->vnum == vnum )
      break;
  return morph;
}

/*
 * These functions wrap the sending of morph stuff, with morphing the code.
 * In most cases this is what should be called in the code when using spells,
 * obj morphing, etc... --Shaddai
 */
int do_morph_char( CHAR_DATA * ch, MORPH_DATA * morph )
{
  bool canmorph = TRUE;
  OBJ_DATA *obj;
  OBJ_DATA *tmpobj;

  if( ch->morph )
    canmorph = FALSE;
  if( morph->obj[0] )
  {
    if( ( obj = get_obj_vnum( ch, morph->obj[0] ) ) == NULL )
      canmorph = FALSE;
    else if( morph->objuse[0] )
    {
      act( AT_OBJECT, "$p disappears in a whisp of smoke!", obj->carried_by, obj, NULL, TO_CHAR );
      if( obj == get_eq_char( obj->carried_by, WEAR_WIELD )
          && ( tmpobj = get_eq_char( obj->carried_by, WEAR_DUAL_WIELD ) ) != NULL )
        tmpobj->wear_loc = WEAR_WIELD;
      separate_obj( obj );
      extract_obj( obj );
    }
  }
  if( morph->obj[1] )
  {
    if( ( obj = get_obj_vnum( ch, morph->obj[1] ) ) == NULL )
      canmorph = FALSE;
    else if( morph->objuse[1] )
    {
      act( AT_OBJECT, "$p disappears in a whisp of smoke!", obj->carried_by, obj, NULL, TO_CHAR );
      if( obj == get_eq_char( obj->carried_by, WEAR_WIELD )
          && ( tmpobj = get_eq_char( obj->carried_by, WEAR_DUAL_WIELD ) ) != NULL )
        tmpobj->wear_loc = WEAR_WIELD;
      separate_obj( obj );
      extract_obj( obj );
    }
  }
  if( morph->obj[2] )
  {
    if( ( obj = get_obj_vnum( ch, morph->obj[2] ) ) == NULL )
      canmorph = FALSE;
    else if( morph->objuse[2] )
    {
      act( AT_OBJECT, "$p disappears in a whisp of smoke!", obj->carried_by, obj, NULL, TO_CHAR );
      if( obj == get_eq_char( obj->carried_by, WEAR_WIELD )
          && ( tmpobj = get_eq_char( obj->carried_by, WEAR_DUAL_WIELD ) ) != NULL )
        tmpobj->wear_loc = WEAR_WIELD;
      separate_obj( obj );
      extract_obj( obj );
    }
  }

  if( morph->hpused )
  {
    if( ch->hit < morph->hpused )
      canmorph = FALSE;
    else
      ch->hit -= morph->hpused;
  }

  if( morph->moveused )
  {
    if( ch->move < morph->moveused )
      canmorph = FALSE;
    else
      ch->move -= morph->moveused;
  }

  if( morph->manaused )
  {
    if( ch->mana < morph->manaused )
      canmorph = FALSE;
    else
      ch->mana -= morph->manaused;
  }

  if( morph->favourused )
  {
    if( IS_NPC( ch ) || !ch->pcdata->deity || ch->pcdata->favor < morph->favourused )
      canmorph = FALSE;
    else
    {
      ch->pcdata->favor -= morph->favourused;
      if( ch->pcdata->favor < ch->pcdata->deity->susceptnum )
        SET_BIT( ch->susceptible, ch->pcdata->deity->suscept );
    }
  }

  if( morph->gloryused )
  {
    if( !IS_NPC( ch ) && ch->pcdata->quest_curr < morph->gloryused )
      canmorph = FALSE;
    else
      ch->pcdata->quest_curr -= morph->gloryused;
  }

  if( !canmorph )
  {
    send_to_char( "You begin to transform, but something goes wrong.\n\r", ch );
    return FALSE;
  }

  send_morph_message( ch, morph, TRUE );
  do_morph( ch, morph );
  return TRUE;
}

void do_unmorph_char( CHAR_DATA * ch )
{
  MORPH_DATA *temp;
  if( !ch->morph )
    return;
  temp = ch->morph->morph;
  do_unmorph( ch );
  send_morph_message( ch, temp, FALSE );
  return;
}

/*
 * This function sends the morph/unmorph message to the people in the room.
 * -- Shaddai
 */
void send_morph_message( CHAR_DATA * ch, MORPH_DATA * morph, bool is_morph )
{
  if( morph == NULL )
    return;
  if( is_morph )
  {
    act( AT_MORPH, morph->morph_other, ch, NULL, NULL, TO_ROOM );
    act( AT_MORPH, morph->morph_self, ch, NULL, NULL, TO_CHAR );
  }
  else
  {
    act( AT_MORPH, morph->unmorph_other, ch, NULL, NULL, TO_ROOM );
    act( AT_MORPH, morph->unmorph_self, ch, NULL, NULL, TO_CHAR );
  }
  return;
}

/*
 * Checks to see if the char meets all the requirements to morph into
 * the provided morph.  Doesn't Look at NPC's class or race as they
 * are different from pc's, but still checks level and if they can
 * cast it or not.  --Shaddai
 */

bool can_morph( CHAR_DATA * ch, MORPH_DATA * morph, bool is_cast )
{
  if( morph == NULL )
    return FALSE;
  if( IS_IMMORTAL( ch ) || IS_NPC( ch ) ) /* Let immortals morph to anything
                                           * Also NPC can do any morph  */
    return TRUE;
  if( morph->no_cast && is_cast )
    return FALSE;
  if( ch->level < morph->level )
    return FALSE;
  if( morph->pkill == ONLY_PKILL && !IS_PKILL( ch ) )
    return FALSE;
  if( morph->pkill == ONLY_PEACEFULL && IS_PKILL( ch ) )
    return FALSE;
  if( morph->sex != -1 && morph->sex != ch->sex )
    return FALSE;
  if( morph->class != 0 && !IS_SET( morph->class, ( 1 << ch->class ) ) )
    return FALSE;
  if( morph->race != 0 && IS_SET( morph->race, ( 1 << ch->race ) ) )
    return FALSE;
  if( morph->deity && ( !ch->pcdata->deity || !get_deity( morph->deity ) ) )
    return FALSE;

  if( morph->timeto != -1 && morph->timefrom != -1 )
  {
    int tmp, i;
    bool found = FALSE;
    /*
     * i is a sanity check, just in case things go haywire so it doesn't
     * * loop forever here. -Shaddai
     */
    for( i = 0, tmp = morph->timefrom; i < 25 && tmp != morph->timeto; i++ )
    {
      if( tmp == time_info.hour )
      {
        found = TRUE;
        break;
      }
      if( tmp == 23 )
        tmp = 0;
      else
        tmp++;
    }
    if( !found )
      return FALSE;
  }

  if( morph->dayfrom != -1 && morph->dayto != -1 &&
      ( morph->dayto < ( time_info.day + 1 ) || morph->dayfrom > ( time_info.day + 1 ) ) )
    return FALSE;
  return TRUE;
}

/*
 * Workhorse of the polymorph code, this turns the player into the proper
 * morph.  Doesn't check if you can morph or not so must use can_morph before
 * this is called.  That is so we can let people morph into things without
 * checking. Also, if anything is changed here, make sure it gets changed
 * in do_unmorph otherwise you will be giving your player stats for free.
 * This also does not send the message to people when you morph good to
 * use in save functions.
 * --Shaddai
 */

void do_morph( CHAR_DATA * ch, MORPH_DATA * morph )
{
  CHAR_MORPH *ch_morph;

  if( !morph )
    return;

  if( ch->morph )
    do_unmorph_char( ch );

  ch_morph = make_char_morph( morph );
  ch->armor += morph->ac;
  ch->mod_str += morph->str;
  ch->mod_int += morph->inte;
  ch->mod_dex += morph->dex;
  ch->mod_lck += morph->lck;
  ch->saving_breath += morph->saving_breath;
  ch->saving_para_petri += morph->saving_para_petri;
  ch->saving_poison_death += morph->saving_poison_death;
  ch->saving_spell_staff += morph->saving_spell_staff;
  ch->saving_wand += morph->saving_wand;
  ch_morph->hitroll = dice_parse( ch, morph->level, morph->hitroll );
  ch->hitroll += ch_morph->hitroll;
  ch_morph->damroll = dice_parse( ch, morph->level, morph->damroll );
  ch->damroll += ch_morph->damroll;
  ch_morph->hit = dice_parse( ch, morph->level, morph->hit );
  if( ( ch->hit + ch_morph->hit ) > 32700 )
    ch_morph->hit = ( 32700 - ch->hit );
  ch->hit += ch_morph->hit;
  ch_morph->move = dice_parse( ch, morph->level, morph->move );
  if( ( ch->move + ch_morph->move ) > 32700 )
    ch_morph->move = ( 32700 - ch->move );
  ch->move += ch_morph->move;
  ch_morph->mana = dice_parse( ch, morph->level, morph->mana );
  if( ( ch->mana + ch_morph->mana ) > 32700 )
    ch_morph->mana = ( 32700 - ch->mana );
  ch->mana += ch_morph->mana;
  xSET_BITS( ch->affected_by, morph->affected_by );
  SET_BIT( ch->immune, morph->immune );
  SET_BIT( ch->resistant, morph->resistant );
  SET_BIT( ch->susceptible, morph->suscept );
  xREMOVE_BITS( ch->affected_by, morph->no_affected_by );
  REMOVE_BIT( ch->immune, morph->no_immune );
  REMOVE_BIT( ch->resistant, morph->no_resistant );
  REMOVE_BIT( ch->susceptible, morph->no_suscept );
  ch->morph = ch_morph;
  morph->used++;
  return;
}

/*
 * This makes sure to take all the affects given to the player by the morph
 * away.  Several things to keep in mind.  If you add something here make
 * sure you add it to do_morph as well (Unless you want to charge your players
 * for morphing ;) ).  Also make sure that their pfile saves with the morph
 * affects off, as the morph may not exist when they log back in.  This
 * function also does not send the message to people when you morph so it is
 * good to use in save functions and other places you don't want people to
 * see the stuff.
 * --Shaddai
 */
void do_unmorph( CHAR_DATA * ch )
{
  CHAR_MORPH *morph;
  if( ( morph = ch->morph ) == NULL )
    return;
  ch->armor -= morph->ac;
  ch->mod_str -= morph->str;
  ch->mod_int -= morph->inte;
  ch->mod_dex -= morph->dex;
  ch->mod_lck -= morph->lck;
  ch->saving_breath -= morph->saving_breath;
  ch->saving_para_petri -= morph->saving_para_petri;
  ch->saving_poison_death -= morph->saving_poison_death;
  ch->saving_spell_staff -= morph->saving_spell_staff;
  ch->saving_wand -= morph->saving_wand;
  ch->hitroll -= morph->hitroll;
  ch->damroll -= morph->damroll;
  ch->hit -= morph->hit;
  ch->move -= morph->move;
  ch->mana -= morph->mana;
  xREMOVE_BITS( ch->affected_by, morph->affected_by );
  REMOVE_BIT( ch->immune, morph->immune );
  REMOVE_BIT( ch->resistant, morph->resistant );
  REMOVE_BIT( ch->susceptible, morph->suscept );
  free_char_morph( ch->morph );
  ch->morph = NULL;
  update_aris( ch );
  return;
}

/*
 * This function saves the morph data.  Should be put in on reboot or shutdown
 * to make use of the sort algorithim. --Shaddai
 */
void save_morphs(  )
{
  MORPH_DATA *morph;
  FILE *fp;

  fclose( fpReserve );
  if( ( fp = fopen( SYSTEM_DIR MORPH_FILE, "w" ) ) == NULL )
  {
    bug( "Save_morph: fopen", 0 );
    perror( SYSTEM_DIR MORPH_FILE );
    fpReserve = fopen( NULL_FILE, "r" );
    return;
  }
  for( morph = morph_start; morph; morph = morph->next )
    fwrite_morph( fp, morph );
  fprintf( fp, "#END\n" );
  fclose( fp );
  fpReserve = fopen( NULL_FILE, "r" );
  return;
}

/* 
 * Write one morph structure to a file. It doesn't print the variable to file
 * if it hasn't been set why waste disk-space :)  --Shaddai
 */

void fwrite_morph( FILE * fp, MORPH_DATA * morph )
{
  if( !morph )
    return;
  fprintf( fp, "Morph           	%s\n", morph->name );
  if( morph->obj[0] != 0 || morph->obj[1] != 0 || morph->obj[2] != 0 )
    fprintf( fp, "Objs  			%d %d %d\n", morph->obj[0], morph->obj[1], morph->obj[2] );
  if( morph->objuse[0] != 0 || morph->objuse[1] != 0 || morph->objuse[2] != 0 )
    fprintf( fp, "Objuse			%d %d %d\n", morph->objuse[0], morph->objuse[1], morph->objuse[2] );
  if( morph->vnum != 0 )
    fprintf( fp, "Vnum              %d\n", morph->vnum );
  if( morph->blood[0] != '\0' )
    fprintf( fp, "Blood    	        %s~\n", morph->blood );
  if( morph->damroll[0] != '\0' )
    fprintf( fp, "Damroll         	%s~\n", morph->damroll );
  if( morph->defpos != POS_STANDING )
    fprintf( fp, "Defpos            %d\n", morph->defpos );
  if( morph->description[0] != '\0' )
    fprintf( fp, "Description     	%s~\n", morph->description );
  if( morph->help[0] != '\0' )
    fprintf( fp, "Help            	%s~\n", morph->help );
  if( morph->hit[0] != '\0' )
    fprintf( fp, "Hit             	%s~\n", morph->hit );
  if( morph->hitroll[0] != '\0' )
    fprintf( fp, "Hitroll         	%s~\n", morph->hitroll );
  if( morph->key_words[0] != '\0' )
    fprintf( fp, "Keywords        	%s~\n", morph->key_words );
  if( morph->long_desc[0] != '\0' )
    fprintf( fp, "Longdesc        	%s~\n", morph->long_desc );
  if( morph->mana[0] != '\0' )
    fprintf( fp, "Mana            	%s~\n", morph->mana );
  if( morph->morph_other[0] != '\0' )
    fprintf( fp, "MorphOther      	%s~\n", morph->morph_other );
  if( morph->morph_self[0] != '\0' )
    fprintf( fp, "MorphSelf       	%s~\n", morph->morph_self );
  if( morph->move[0] != '\0' )
    fprintf( fp, "Move            	%s~\n", morph->move );
  if( morph->no_skills[0] != '\0' )
    fprintf( fp, "NoSkills         %s~\n", morph->no_skills );
  if( morph->short_desc[0] != '\0' )
    fprintf( fp, "ShortDesc       	%s~\n", morph->short_desc );
  if( morph->skills[0] != '\0' )
    fprintf( fp, "Skills          	%s~\n", morph->skills );
  if( morph->unmorph_other[0] != '\0' )
    fprintf( fp, "UnmorphOther    	%s~\n", morph->unmorph_other );
  if( morph->unmorph_self[0] != '\0' )
    fprintf( fp, "UnmorphSelf     	%s~\n", morph->unmorph_self );
  if( !xIS_EMPTY( morph->affected_by ) )
    fprintf( fp, "Affected        	%s\n", print_bitvector( &morph->affected_by ) );
  if( morph->class != 0 )
    fprintf( fp, "Class           	%s~\n", class_string( morph->class ) );
  if( morph->immune != 0 )
    fprintf( fp, "Immune          	%d\n", morph->immune );
  if( !xIS_EMPTY( morph->no_affected_by ) )
    fprintf( fp, "NoAffected      	%s\n", print_bitvector( &morph->no_affected_by ) );
  if( morph->no_immune != 0 )
    fprintf( fp, "NoImmune        	%d\n", morph->no_immune );
  if( morph->no_resistant != 0 )
    fprintf( fp, "NoResistant     	%d\n", morph->no_resistant );
  if( morph->no_suscept != 0 )
    fprintf( fp, "NoSuscept       	%d\n", morph->no_suscept );
  if( morph->race != 0 )
    fprintf( fp, "Race        	%s~\n", race_string( morph->race ) );
  if( morph->resistant != 0 )
    fprintf( fp, "Resistant       	%d\n", morph->resistant );
  if( morph->suscept != 0 )
    fprintf( fp, "Suscept        	%d\n", morph->suscept );
  if( morph->used != 0 )
    fprintf( fp, "Used       	%d\n", morph->used );
  if( morph->sex != -1 )
    fprintf( fp, "Sex		%d\n", morph->sex );
  if( morph->pkill != 0 )
    fprintf( fp, "Pkill		%d\n", morph->pkill );
  if( morph->timefrom != -1 )
    fprintf( fp, "TimeFrom		%d\n", morph->timefrom );
  if( morph->timeto != -1 )
    fprintf( fp, "TimeTo		%d\n", morph->timeto );
  if( morph->dayfrom != -1 )
    fprintf( fp, "DayFrom		%d\n", morph->dayfrom );
  if( morph->dayto != -1 )
    fprintf( fp, "DayTo		%d\n", morph->dayto );
  if( morph->bloodused != 0 )
    fprintf( fp, "BloodUsed	%d\n", morph->bloodused );
  if( morph->manaused != 0 )
    fprintf( fp, "ManaUsed		%d\n", morph->manaused );
  if( morph->moveused != 0 )
    fprintf( fp, "MoveUsed		%d\n", morph->moveused );
  if( morph->hpused != 0 )
    fprintf( fp, "HpUsed		%d\n", morph->hpused );
  if( morph->favourused != 0 )
    fprintf( fp, "FavourUsed	%d\n", morph->favourused );
  if( morph->gloryused != 0 )
    fprintf( fp, "GloryUsed	%d\n", morph->gloryused );
  if( morph->ac != 0 )
    fprintf( fp, "Armor        	%d\n", morph->ac );
  if( morph->con != 0 )
    fprintf( fp, "Constitution    	%d\n", morph->con );
  if( morph->dex != 0 )
    fprintf( fp, "Dexterity       	%d\n", morph->dex );
  if( morph->dodge != 0 )
    fprintf( fp, "Dodge        	%d\n", morph->dodge );
  if( morph->inte != 0 )
    fprintf( fp, "Intelligence    	%d\n", morph->inte );
  if( morph->lck != 0 )
    fprintf( fp, "Luck        	%d\n", morph->lck );
  if( morph->level != 0 )
    fprintf( fp, "Level        	%d\n", morph->level );
  if( morph->parry != 0 )
    fprintf( fp, "Parry        	%d\n", morph->parry );
  if( morph->saving_breath != 0 )
    fprintf( fp, "SaveBreath      	%d\n", morph->saving_breath );
  if( morph->saving_para_petri != 0 )
    fprintf( fp, "SavePara        	%d\n", morph->saving_para_petri );
  if( morph->saving_poison_death != 0 )
    fprintf( fp, "SavePoison      	%d\n", morph->saving_poison_death );
  if( morph->saving_spell_staff != 0 )
    fprintf( fp, "SaveSpell       	%d\n", morph->saving_spell_staff );
  if( morph->saving_wand != 0 )
    fprintf( fp, "SaveWand        	%d\n", morph->saving_wand );
  if( morph->str != 0 )
    fprintf( fp, "Strength        	%d\n", morph->str );
  if( morph->tumble != 0 )
    fprintf( fp, "Tumble          	%d\n", morph->tumble );
  if( morph->no_cast )
    fprintf( fp, "NoCast          	%d\n", morph->no_cast );
  fprintf( fp, "End\n\n" );
  return;
}

/*
 *  This function loads in the morph data.  Note that this function MUST be
 *  used AFTER the race and class tables have been read in and setup.
 *  --Shaddai
 */

void load_morphs(  )
{
  MORPH_DATA *morph;
  char buf[MAX_INPUT_LENGTH];
  char *word;
  FILE *fp;
  bool my_continue = TRUE;
  bool fMatch = FALSE;

  if( ( fp = fopen( SYSTEM_DIR MORPH_FILE, "r" ) ) == NULL )
  {
    bug( "Load_morph: fopen", 0 );
    perror( SYSTEM_DIR MORPH_FILE );
    return;
  }

  while( my_continue )
  {
    morph = NULL;
    word = feof( fp ) ? "#END" : fread_word( fp );
    fMatch = FALSE;

    switch ( UPPER( word[0] ) )
    {
      case '#':
        if( !str_cmp( word, "#END" ) )
        {
          fclose( fp );
          fMatch = TRUE;
          my_continue = FALSE;
          break;
        }
        break;
      case 'M':
        if( !str_cmp( word, "Morph" ) )
        {
          morph = fread_morph( fp );
          fMatch = TRUE;
        }
        break;
    }
    if( !fMatch )
    {
      sprintf( buf, "Fread_morph: no match: %s", word );
      bug( buf, 0 );
    }
    if( morph )
      LINK( morph, morph_start, morph_end, next, prev );
  }
  if( sysdata.morph_opt )
  {
    log_string( "Optimizing Morphs." );
    sort_morphs(  );
  }
  setup_morph_vnum(  );
  log_string( "Done." );
  return;
}

/*
 * Read in one morph structure
 */

MORPH_DATA *fread_morph( FILE * fp )
{
  MORPH_DATA *morph;
  char buf[MAX_INPUT_LENGTH];
  char *arg;
  char temp[MAX_STRING_LENGTH];
  char *word;
  int i;
  bool fMatch;

  word = feof( fp ) ? "End" : fread_word( fp );

  if( !str_cmp( word, "End" ) )
    return NULL;

  CREATE( morph, MORPH_DATA, 1 );
  morph_defaults( morph );
  morph->name = str_dup( word );

  for( ;; )
  {
    word = feof( fp ) ? "End" : fread_word( fp );
    fMatch = FALSE;
    switch ( UPPER( word[0] ) )
    {
      case 'A':
        KEY( "Armor", morph->ac, fread_number( fp ) );
        KEY( "Affected", morph->affected_by, fread_bitvector( fp ) );
        break;
      case 'B':
        KEY( "Blood", morph->blood, fread_string_nohash( fp ) );
        KEY( "BloodUsed", morph->bloodused, fread_number( fp ) );
        break;
      case 'C':
        if( !str_cmp( word, "Class" ) )
        {
          fMatch = TRUE;
          arg = fread_string_nohash( fp );
          arg = one_argument( arg, temp );
          while( temp[0] != '\0' )
          {
            for( i = 0; i < MAX_PC_CLASS; i++ )
              if( !str_cmp( temp, class_table[i]->who_name ) )
              {
                SET_BIT( morph->class, ( 1 << i ) );
                break;
              }
            arg = one_argument( arg, temp );
          }
        }
        KEY( "Constitution", morph->con, fread_number( fp ) );
        break;
      case 'D':
        KEY( "Damroll", morph->damroll, fread_string_nohash( fp ) );
        KEY( "DayFrom", morph->dayfrom, fread_number( fp ) );
        KEY( "DayTo", morph->dayto, fread_number( fp ) );
        KEY( "Defpos", morph->defpos, fread_number( fp ) );
        KEY( "Description", morph->description, fread_string_nohash( fp ) );
        KEY( "Dexterity", morph->dex, fread_number( fp ) );
        KEY( "Dodge", morph->dodge, fread_number( fp ) );
        break;
      case 'E':
        if( !str_cmp( word, "End" ) )
          return morph;
        break;
      case 'F':
        KEY( "FavourUsed", morph->favourused, fread_number( fp ) );
        break;
      case 'G':
        KEY( "GloryUsed", morph->gloryused, fread_number( fp ) );
        break;
      case 'H':
        KEY( "Help", morph->help, fread_string_nohash( fp ) );
        KEY( "Hit", morph->hit, fread_string_nohash( fp ) );
        KEY( "Hitroll", morph->hitroll, fread_string_nohash( fp ) );
        KEY( "HpUsed", morph->hpused, fread_number( fp ) );
        break;
      case 'I':
        KEY( "Intelligence", morph->inte, fread_number( fp ) );
        KEY( "Immune", morph->immune, fread_number( fp ) );
        break;
      case 'K':
        KEY( "Keywords", morph->key_words, fread_string_nohash( fp ) );
        break;
      case 'L':
        KEY( "Level", morph->level, fread_number( fp ) );
        KEY( "Longdesc", morph->long_desc, fread_string_nohash( fp ) );
        KEY( "Luck", morph->lck, fread_number( fp ) );
        break;
      case 'M':
        KEY( "Mana", morph->mana, fread_string_nohash( fp ) );
        KEY( "ManaUsed", morph->manaused, fread_number( fp ) );
        KEY( "MorphOther", morph->morph_other, fread_string_nohash( fp ) );
        KEY( "MorphSelf", morph->morph_self, fread_string_nohash( fp ) );
        KEY( "Move", morph->morph_self, fread_string_nohash( fp ) );
        KEY( "MoveUsed", morph->moveused, fread_number( fp ) );
        break;
      case 'N':
        KEY( "NoAffected", morph->no_affected_by, fread_bitvector( fp ) );
        KEY( "NoImmune", morph->no_immune, fread_number( fp ) );
        KEY( "NoResistant", morph->no_resistant, fread_number( fp ) );
        KEY( "NoSkills", morph->no_skills, fread_string_nohash( fp ) );
        KEY( "NoSuscept", morph->no_suscept, fread_number( fp ) );
        if( !str_cmp( word, "NoCast" ) )
        {
          fMatch = TRUE;
          morph->no_cast = fread_number( fp );
        }
        break;
      case 'O':
        if( !str_cmp( word, "Objs" ) )
        {
          fMatch = TRUE;
          morph->obj[0] = fread_number( fp );
          morph->obj[1] = fread_number( fp );
          morph->obj[2] = fread_number( fp );
        }
        if( !str_cmp( word, "Objuse" ) )
        {
          fMatch = TRUE;
          morph->objuse[0] = fread_number( fp );
          morph->objuse[1] = fread_number( fp );
          morph->objuse[2] = fread_number( fp );
        }
        break;
      case 'P':
        KEY( "Parry", morph->parry, fread_number( fp ) );
        KEY( "Pkill", morph->pkill, fread_number( fp ) );
        break;
      case 'R':
        if( !str_cmp( word, "Race" ) )
        {
          fMatch = TRUE;
          arg = fread_string_nohash( fp );
          arg = one_argument( arg, temp );
          while( temp[0] != '\0' )
          {
            for( i = 0; i < MAX_PC_RACE; i++ )
              if( !str_cmp( temp, race_table[i]->race_name ) )
              {
                SET_BIT( morph->race, ( 1 << i ) );
                break;
              }
            arg = one_argument( arg, temp );
          }
        }
        KEY( "Resistant", morph->resistant, fread_number( fp ) );
        break;
      case 'S':
        KEY( "SaveBreath", morph->saving_breath, fread_number( fp ) );
        KEY( "SavePara", morph->saving_para_petri, fread_number( fp ) );
        KEY( "SavePoison", morph->saving_poison_death, fread_number( fp ) );
        KEY( "SaveSpell", morph->saving_spell_staff, fread_number( fp ) );
        KEY( "SaveWand", morph->saving_wand, fread_number( fp ) );
        KEY( "Sex", morph->sex, fread_number( fp ) );
        KEY( "ShortDesc", morph->short_desc, fread_string_nohash( fp ) );
        KEY( "Skills", morph->skills, fread_string_nohash( fp ) );
        KEY( "Strength", morph->str, fread_number( fp ) );
        KEY( "Suscept", morph->suscept, fread_number( fp ) );
        break;
      case 'T':
        KEY( "TimeFrom", morph->timefrom, fread_number( fp ) );
        KEY( "TimeTo", morph->timeto, fread_number( fp ) );
        KEY( "Tumble", morph->tumble, fread_number( fp ) );
        break;
      case 'U':
        KEY( "UnmorphOther", morph->unmorph_other, fread_string_nohash( fp ) );
        KEY( "UnmorphSelf", morph->unmorph_self, fread_string_nohash( fp ) );
        KEY( "Used", morph->used, fread_number( fp ) );
        break;
      case 'V':
        KEY( "Vnum", morph->vnum, fread_number( fp ) );
        break;
      case 'W':
        break;
    }
    if( !fMatch )
    {
      sprintf( buf, "Fread_morph: no match: %s", word );
      bug( buf, 0 );
      /*
       * Bailing out on this morph as something may be messed up 
       * * this is going to have lots of bugs from the load_morphs this
       * * way, but it is better than possibly having the memory messed
       * * up! --Shaddai
       */
      free_morph( morph );
      return NULL;
    }
  }
}

/* 
 * Function that releases all the memory for a morph struct.  Carefull not
 * to use the memory afterwards as it doesn't exist.
 * --Shaddai 
 */
void free_morph( MORPH_DATA * morph )
{
  if( !morph )
    return;
  if( morph->blood )
    DISPOSE( morph->blood );
  if( morph->damroll )
    DISPOSE( morph->damroll );
  if( morph->description )
    DISPOSE( morph->description );
  if( morph->help )
    DISPOSE( morph->help );
  if( morph->hit )
    DISPOSE( morph->hit );
  if( morph->hitroll )
    DISPOSE( morph->hitroll );
  if( morph->key_words )
    DISPOSE( morph->key_words );
  if( morph->long_desc )
    DISPOSE( morph->long_desc );
  if( morph->mana )
    DISPOSE( morph->mana );
  if( morph->morph_other )
    DISPOSE( morph->morph_other );
  if( morph->morph_self )
    DISPOSE( morph->morph_self );
  if( morph->move )
    DISPOSE( morph->move );
  if( morph->name )
    DISPOSE( morph->name );
  if( morph->short_desc )
    DISPOSE( morph->short_desc );
  if( morph->skills )
    DISPOSE( morph->skills );
  if( morph->no_skills )
    DISPOSE( morph->no_skills );
  if( morph->unmorph_other )
    DISPOSE( morph->unmorph_other );
  if( morph->unmorph_self )
    DISPOSE( morph->unmorph_self );
  DISPOSE( morph );
  return;
}

/*
 * This function set's up all the default values for a morph
 */
void morph_defaults( MORPH_DATA * morph )
{
  morph->blood = str_dup( "" );
  morph->damroll = str_dup( "" );
  morph->description = str_dup( "" );
  morph->help = str_dup( "" );
  morph->hit = str_dup( "" );
  morph->hitroll = str_dup( "" );
  morph->key_words = str_dup( "" );
  morph->long_desc = str_dup( "" );
  morph->mana = str_dup( "" );
  morph->morph_other = str_dup( "" );
  morph->morph_self = str_dup( "" );
  morph->move = str_dup( "" );
  morph->name = str_dup( "" );
  morph->short_desc = str_dup( "" );
  morph->skills = str_dup( "" );
  morph->no_skills = str_dup( "" );
  morph->unmorph_other = str_dup( "" );
  morph->unmorph_self = str_dup( "" );
  xCLEAR_BITS( morph->affected_by );
  morph->class = 0;
  morph->sex = -1;
  morph->timefrom = -1;
  morph->timeto = -1;
  morph->dayfrom = -1;
  morph->dayto = -1;
  morph->pkill = 0;
  morph->bloodused = 0;
  morph->manaused = 0;
  morph->moveused = 0;
  morph->hpused = 0;
  morph->favourused = 0;
  morph->gloryused = 0;
  morph->immune = 0;
  xCLEAR_BITS( morph->no_affected_by );
  morph->no_immune = 0;
  morph->no_resistant = 0;
  morph->no_suscept = 0;
  morph->obj[0] = 0;
  morph->obj[1] = 0;
  morph->obj[2] = 0;
  morph->objuse[0] = FALSE;
  morph->objuse[1] = FALSE;
  morph->objuse[2] = FALSE;
  morph->race = 0;
  morph->resistant = 0;
  morph->suscept = 0;
  morph->used = 0;
  morph->ac = 0;
  morph->defpos = POS_STANDING;
  morph->dex = 0;
  morph->dodge = 0;
  morph->con = 0;
  morph->inte = 0;
  morph->lck = 0;
  morph->level = 0;
  morph->parry = 0;
  morph->saving_breath = 0;
  morph->saving_para_petri = 0;
  morph->saving_poison_death = 0;
  morph->saving_spell_staff = 0;
  morph->saving_wand = 0;
  morph->str = 0;
  morph->tumble = 0;
  morph->no_cast = FALSE;
  morph->timer = -1;
  morph->vnum = 0;
  return;
}

/*
 * This function copys one morph structure to another
 */
void copy_morph( MORPH_DATA * morph, MORPH_DATA * temp )
{
  morph->blood = str_dup( temp->blood );
  morph->damroll = str_dup( temp->damroll );
  morph->description = str_dup( temp->description );
  morph->help = str_dup( temp->help );
  morph->hit = str_dup( temp->hit );
  morph->hitroll = str_dup( temp->hitroll );
  morph->key_words = str_dup( temp->key_words );
  morph->long_desc = str_dup( temp->long_desc );
  morph->mana = str_dup( temp->mana );
  morph->morph_other = str_dup( temp->morph_other );
  morph->morph_self = str_dup( temp->morph_self );
  morph->move = str_dup( temp->move );
  morph->name = str_dup( temp->name );
  morph->short_desc = str_dup( temp->short_desc );
  morph->skills = str_dup( temp->skills );
  morph->no_skills = str_dup( temp->no_skills );
  morph->unmorph_other = str_dup( temp->unmorph_other );
  morph->unmorph_self = str_dup( temp->unmorph_self );
  morph->affected_by = temp->affected_by;
  morph->class = temp->class;
  morph->sex = temp->sex;
  morph->timefrom = temp->timefrom;
  morph->timeto = temp->timeto;
  morph->timefrom = temp->timefrom;
  morph->dayfrom = temp->dayfrom;
  morph->dayto = temp->dayto;
  morph->pkill = temp->pkill;
  morph->manaused = temp->manaused;
  morph->bloodused = temp->bloodused;
  morph->moveused = temp->moveused;
  morph->hpused = temp->hpused;
  morph->favourused = temp->favourused;
  morph->gloryused = temp->gloryused;
  morph->immune = temp->immune;
  morph->no_affected_by = temp->no_affected_by;
  morph->no_immune = temp->no_immune;
  morph->no_resistant = temp->no_resistant;
  morph->no_suscept = temp->no_suscept;
  morph->obj[0] = temp->obj[0];
  morph->obj[1] = temp->obj[1];
  morph->obj[2] = temp->obj[2];
  morph->objuse[0] = temp->objuse[0];
  morph->objuse[1] = temp->objuse[1];
  morph->objuse[2] = temp->objuse[2];
  morph->race = temp->race;
  morph->resistant = temp->resistant;
  morph->suscept = temp->suscept;
  morph->ac = temp->ac;
  morph->defpos = temp->defpos;
  morph->dex = temp->dex;
  morph->dodge = temp->dodge;
  morph->con = temp->con;
  morph->inte = temp->inte;
  morph->lck = temp->lck;
  morph->level = temp->level;
  morph->parry = temp->parry;
  morph->saving_breath = temp->saving_breath;
  morph->saving_para_petri = temp->saving_para_petri;
  morph->saving_poison_death = temp->saving_poison_death;
  morph->saving_spell_staff = temp->saving_spell_staff;
  morph->saving_wand = temp->saving_wand;
  morph->str = temp->str;
  morph->tumble = temp->tumble;
  morph->no_cast = temp->no_cast;
  morph->timer = temp->timer;
  return;
}

/*
 * This is to move the more used morphs to the beginning hopefully saving
 * that .000000001 second ;) --Shaddai
 */

void sort_morphs(  )
{
  MORPH_DATA *morph, *temp;
  int fMatch;

  /*
   * This is slow and the wrong way to code it but I am tired and have more
   * * Important things to-do :)  Also it is only done at boot-up so who cares
   * * if it is slow or not. --Shaddai
   */

  do
  {
    fMatch = FALSE;
    if( morph_start == NULL )
      return;
    for( morph = morph_start, temp = morph->next; morph && temp; morph = morph->next, temp = temp->next )
    {
      if( morph->used < temp->used )
      {
        if( morph == morph_start )
          morph_start = temp;
        if( temp == morph_end )
          morph_end = morph;
        if( morph->prev )
          morph->prev->next = temp;
        if( temp->next )
          temp->next->prev = morph;
        temp->prev = morph->prev;
        morph->next = temp->next;
        temp->next = morph;
        morph->prev = temp;
        fMatch = TRUE;
        break;
      }
    }
  }
  while( fMatch );
  return;
}

/*
 * Create new player morph, a scailed down version of original morph
 * so if morph gets changed stats don't get messed up.
 */
CHAR_MORPH *make_char_morph( MORPH_DATA * morph )
{
  CHAR_MORPH *ch_morph;

  if( morph == NULL )
    return NULL;

  CREATE( ch_morph, CHAR_MORPH, 1 );
  ch_morph->morph = morph;
  ch_morph->ac = morph->ac;
  ch_morph->str = morph->str;
  ch_morph->inte = morph->inte;
  ch_morph->dex = morph->dex;
  ch_morph->lck = morph->lck;
  ch_morph->saving_breath = morph->saving_breath;
  ch_morph->saving_para_petri = morph->saving_para_petri;
  ch_morph->saving_poison_death = morph->saving_poison_death;
  ch_morph->saving_spell_staff = morph->saving_spell_staff;
  ch_morph->saving_wand = morph->saving_wand;
  ch_morph->timer = morph->timer;
  ch_morph->hitroll = 0;
  ch_morph->damroll = 0;
  ch_morph->hit = 0;
  ch_morph->mana = 0;
  ch_morph->move = 0;
  ch_morph->blood = 0;
  ch_morph->affected_by = morph->affected_by;
  ch_morph->immune = morph->immune;
  ch_morph->resistant = morph->resistant;
  ch_morph->suscept = morph->suscept;
  ch_morph->no_affected_by = morph->no_affected_by;
  ch_morph->no_immune = morph->no_immune;
  ch_morph->no_resistant = morph->no_resistant;
  ch_morph->no_suscept = morph->no_suscept;
  return ch_morph;
}

/*
 * Player command to create a new morph
 */
void do_morphcreate( CHAR_DATA * ch, char *argument )
{
  MORPH_DATA *morph;
  MORPH_DATA *temp = NULL;
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];

  argument = one_argument( argument, arg1 );
  argument = one_argument( argument, arg2 );

  if( arg1[0] == '\0' )
  {
    send_to_char( "Usage: morphcreate <name>\n\r", ch );
    send_to_char( "Usage: morphcreate <name/vnum> copy\n\r", ch );
    return;
  }

  if( arg2[0] != '\0' && !str_cmp( arg2, "copy" ) )
  {
    if( is_number( arg1 ) )
    {
      if( ( temp = get_morph_vnum( atoi( arg1 ) ) ) == NULL )
      {
        ch_printf( ch, "No such morph vnum %d exists.\n\r", atoi( arg1 ) );
        return;
      }
    }
    else if( ( temp = get_morph( arg1 ) ) == NULL )
    {
      ch_printf( ch, "No such morph %s exists.\n\r", arg1 );
      return;
    }
  }
  smash_tilde( arg1 );
  CREATE( morph, MORPH_DATA, 1 );
  morph_defaults( morph );
  if( morph->name )
    DISPOSE( morph->name );
  if( arg2[0] != '\0' && !str_cmp( arg2, "copy" ) && temp )
    copy_morph( morph, temp );
  else
    morph->name = str_dup( arg1 );
  morph->vnum = morph_vnum;
  morph_vnum++;
  LINK( morph, morph_start, morph_end, next, prev );
  ch_printf( ch, "Morph %s created with vnum %d.\n\r", morph->name, morph->vnum );
  return;
}

/*
 * Player function to delete a morph. --Shaddai
 * NOTE Need to check all players and force them to unmorph first
 */

void do_morphdestroy( CHAR_DATA * ch, char *argument )
{
  MORPH_DATA *morph;
  char arg[MAX_INPUT_LENGTH];

  one_argument( argument, arg );

  if( is_number( arg ) )
    morph = get_morph_vnum( atoi( arg ) );
  else
    morph = get_morph( arg );

  if( !morph )
  {
    ch_printf( ch, "Unkown morph %s.\n\r", arg );
    return;
  }
  unmorph_all( morph );
  UNLINK( morph, morph_start, morph_end, next, prev );
  free_morph( morph );
  send_to_char( "Morph deleted.\n\r", ch );
  return;
}

void free_char_morph( CHAR_MORPH * morph )
{
  /*
   * Boy this is easy but better to keep it a seperate function for later
   * * possible complications.  --Shaddai
   */
  DISPOSE( morph );
  return;
}

void fwrite_morph_data( CHAR_DATA * ch, FILE * fp )
{
  CHAR_MORPH *morph;
  if( ch->morph == NULL )
    return;
  morph = ch->morph;
  fprintf( fp, "#MorphData\n" );
  /*
   * Only Print Out what is necessary 
   */
  if( morph->morph != NULL )
  {
    fprintf( fp, "Vnum %d\n", morph->morph->vnum );
    fprintf( fp, "Name %s~\n", morph->morph->name );
  }
  if( !xIS_EMPTY( morph->affected_by ) )
    fprintf( fp, "Affect          %s\n", print_bitvector( &morph->affected_by ) );
  if( morph->immune != 0 )
    fprintf( fp, "Immune          %d\n", morph->immune );
  if( morph->resistant != 0 )
    fprintf( fp, "Resistant       %d\n", morph->resistant );
  if( morph->suscept != 0 )
    fprintf( fp, "Suscept         %d\n", morph->suscept );
  if( !xIS_EMPTY( morph->no_affected_by ) )
    fprintf( fp, "NoAffect        %s\n", print_bitvector( &morph->no_affected_by ) );
  if( morph->no_immune != 0 )
    fprintf( fp, "NoImmune        %d\n", morph->no_immune );
  if( morph->no_resistant != 0 )
    fprintf( fp, "NoResistant     %d\n", morph->no_resistant );
  if( morph->no_suscept != 0 )
    fprintf( fp, "NoSuscept       %d\n", morph->no_suscept );
  if( morph->ac != 0 )
    fprintf( fp, "Armor           %d\n", morph->ac );
  if( morph->blood != 0 )
    fprintf( fp, "Blood           %d\n", morph->blood );
  if( morph->con != 0 )
    fprintf( fp, "Constitution    %d\n", morph->con );
  if( morph->damroll != 0 )
    fprintf( fp, "Damroll	 %d\n", morph->damroll );
  if( morph->dex != 0 )
    fprintf( fp, "Dexterity       %d\n", morph->dex );
  if( morph->dodge != 0 )
    fprintf( fp, "Dodge           %d\n", morph->dodge );
  if( morph->hit != 0 )
    fprintf( fp, "Hit             %d\n", morph->hit );
  if( morph->hitroll != 0 )
    fprintf( fp, "Hitroll         %d\n", morph->hitroll );
  if( morph->inte != 0 )
    fprintf( fp, "Intelligence    %d\n", morph->inte );
  if( morph->lck != 0 )
    fprintf( fp, "Luck            %d\n", morph->lck );
  if( morph->mana != 0 )
    fprintf( fp, "Mana            %d\n", morph->mana );
  if( morph->move != 0 )
    fprintf( fp, "Move            %d\n", morph->move );
  if( morph->parry != 0 )
    fprintf( fp, "Parry           %d\n", morph->parry );
  if( morph->saving_breath != 0 )
    fprintf( fp, "Save1           %d\n", morph->saving_breath );
  if( morph->saving_para_petri != 0 )
    fprintf( fp, "Save2           %d\n", morph->saving_para_petri );
  if( morph->saving_poison_death != 0 )
    fprintf( fp, "Save3           %d\n", morph->saving_poison_death );
  if( morph->saving_spell_staff != 0 )
    fprintf( fp, "Save4           %d\n", morph->saving_spell_staff );
  if( morph->saving_wand != 0 )
    fprintf( fp, "Save5           %d\n", morph->saving_wand );
  if( morph->str != 0 )
    fprintf( fp, "Strength        %d\n", morph->str );
  if( morph->timer != -1 )
    fprintf( fp, "Timer	       %d\n", morph->timer );
  if( morph->tumble != 0 )
    fprintf( fp, "Tumble          %d\n", morph->tumble );
  fprintf( fp, "End\n" );
  return;
}

void fread_morph_data( CHAR_DATA * ch, FILE * fp )
{
  CHAR_MORPH *morph;
  bool fMatch;
  char *word;
  char buf[MAX_STRING_LENGTH];

  CREATE( morph, CHAR_MORPH, 1 );
  clear_char_morph( morph );
  ch->morph = morph;
  for( ;; )
  {
    word = feof( fp ) ? "End" : fread_word( fp );
    fMatch = FALSE;

    switch ( UPPER( word[0] ) )
    {
      case 'A':
        KEY( "Affect", morph->affected_by, fread_bitvector( fp ) );
        KEY( "Armor", morph->ac, fread_number( fp ) );
        break;
      case 'B':
        KEY( "Blood", morph->blood, fread_number( fp ) );
        break;
      case 'C':
        KEY( "Constitution", morph->con, fread_number( fp ) );
        break;
      case 'D':
        KEY( "Damroll", morph->damroll, fread_number( fp ) );
        KEY( "Dexterity", morph->dex, fread_number( fp ) );
        KEY( "Dodge", morph->dodge, fread_number( fp ) );
        break;
      case 'E':
        if( !str_cmp( "End", word ) )
          return;
        break;
      case 'H':
        KEY( "Hit", morph->hit, fread_number( fp ) );
        KEY( "Hitroll", morph->hitroll, fread_number( fp ) );
        break;
      case 'I':
        KEY( "Immune", morph->immune, fread_number( fp ) );
        KEY( "Intelligence", morph->inte, fread_number( fp ) );
        break;
      case 'L':
        KEY( "Luck", morph->lck, fread_number( fp ) );
        break;
      case 'M':
        KEY( "Mana", morph->mana, fread_number( fp ) );
        KEY( "Move", morph->move, fread_number( fp ) );
        break;
      case 'N':
        if( !str_cmp( "Name", word ) )
        {
          fMatch = TRUE;
          if( morph->morph )
            if( str_cmp( morph->morph->name, fread_string( fp ) ) )
              bug( "Morph Name doesn't match vnum %d.", morph->morph->vnum );
        }
        KEY( "NoAffect", morph->no_affected_by, fread_bitvector( fp ) );
        KEY( "NoImmune", morph->no_immune, fread_number( fp ) );
        KEY( "NoResistant", morph->no_resistant, fread_number( fp ) );
        KEY( "NoSuscept", morph->no_suscept, fread_number( fp ) );
        break;
      case 'P':
        KEY( "Parry", morph->parry, fread_number( fp ) );
        break;
      case 'R':
        KEY( "Resistant", morph->resistant, fread_number( fp ) );
        break;
      case 'S':
        KEY( "Save1", morph->saving_breath, fread_number( fp ) );
        KEY( "Save2", morph->saving_para_petri, fread_number( fp ) );
        KEY( "Save3", morph->saving_poison_death, fread_number( fp ) );
        KEY( "Save4", morph->saving_spell_staff, fread_number( fp ) );
        KEY( "Save5", morph->saving_wand, fread_number( fp ) );
        KEY( "Strength", morph->str, fread_number( fp ) );
        KEY( "Suscept", morph->suscept, fread_number( fp ) );
        break;
      case 'T':
        KEY( "Timer", morph->timer, fread_number( fp ) );
        KEY( "Tumble", morph->tumble, fread_number( fp ) );
        break;
      case 'V':
        if( !str_cmp( "Vnum", word ) )
        {
          fMatch = TRUE;
          morph->morph = get_morph_vnum( fread_number( fp ) );
        }
        break;
      case 'W':
        break;
    }
    if( !fMatch )
    {
      sprintf( buf, "Fread_morph_data: no match: %s", word );
      bug( buf, 0 );
    }
  }
  return;
}

void clear_char_morph( CHAR_MORPH * morph )
{
  morph->timer = -1;
  xCLEAR_BITS( morph->affected_by );
  xCLEAR_BITS( morph->no_affected_by );
  morph->immune = 0;
  morph->no_immune = 0;
  morph->no_resistant = 0;
  morph->no_suscept = 0;
  morph->resistant = 0;
  morph->suscept = 0;
  morph->ac = 0;
  morph->blood = 0;
  morph->con = 0;
  morph->damroll = 0;
  morph->dex = 0;
  morph->dodge = 0;
  morph->hit = 0;
  morph->hitroll = 0;
  morph->inte = 0;
  morph->lck = 0;
  morph->mana = 0;
  morph->parry = 0;
  morph->saving_breath = 0;
  morph->saving_para_petri = 0;
  morph->saving_poison_death = 0;
  morph->saving_spell_staff = 0;
  morph->saving_wand = 0;
  morph->str = 0;
  morph->tumble = 0;
  morph->morph = NULL;
  return;
}

void setup_morph_vnum( void )
{
  MORPH_DATA *morph;
  int vnum;
  vnum = morph_vnum;

  for( morph = morph_start; morph; morph = morph->next )
    if( morph->vnum > vnum )
      vnum = morph->vnum;
  if( vnum < 1000 )
    vnum = 1000;
  else
    vnum++;

  for( morph = morph_start; morph; morph = morph->next )
    if( morph->vnum == 0 )
    {
      morph->vnum = vnum;
      vnum++;
    }
  morph_vnum = vnum;
  return;
}

void unmorph_all( MORPH_DATA * morph )
{
  CHAR_DATA *vch;

  for( vch = first_char; vch; vch = vch->next )
  {
    if( IS_NPC( vch ) )
      continue;
    if( vch->morph == NULL || vch->morph->morph == NULL || vch->morph->morph != morph )
      continue;
    do_unmorph_char( vch );
  }
  return;
}

/* 
 * Following functions are for immortal testing purposes.
 */

void do_imm_morph( CHAR_DATA * ch, char *argument )
{
  MORPH_DATA *morph;
  CHAR_DATA *victim = NULL;
  char arg[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int vnum;

  if( IS_NPC( ch ) )
  {
    send_to_char( "Only player characters can use this command.\n\r", ch );
    return;
  }

  argument = one_argument( argument, arg );
  one_argument( argument, arg2 );

  if( !is_number( arg ) )
  {
    send_to_char( "Syntax: morph <vnum>\n\r", ch );
    return;
  }
  vnum = atoi( arg );
  morph = get_morph_vnum( vnum );

  if( morph == NULL )
  {
    ch_printf( ch, "No such morph %d exists.\n\r", vnum );
    return;
  }
  if( arg2[0] == '\0' )
    do_morph_char( ch, morph );
  else if( ( victim = get_char_world( ch, arg2 ) ) == NULL )
  {
    send_to_char( "No one like that in all the realms.\n\r", ch );
    return;
  }
  if( victim != NULL && get_trust( ch ) < get_trust( victim ) && !IS_NPC( victim ) )
  {
    send_to_char( "You can't do that!\n\r", ch );
    return;
  }
  else if( victim != NULL )
    do_morph_char( victim, morph );
  send_to_char( "Done.\n\r", ch );
  return;
}

/*
 * This is just a wrapper.  --Shaddai
 */

void do_imm_unmorph( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim = NULL;
  char arg[MAX_INPUT_LENGTH];

  one_argument( argument, arg );
  if( arg[0] == '\0' )
    do_unmorph_char( ch );
  else if( ( victim = get_char_world( ch, arg ) ) == NULL )
  {
    send_to_char( "No one like that in all the realms.\n\r", ch );
    return;
  }
  if( victim != NULL && get_trust( ch ) < get_trust( victim ) && !IS_NPC( victim ) )
  {
    send_to_char( "You can't do that!\n\r", ch );
    return;
  }
  else if( victim != NULL )
    do_unmorph_char( victim );
  send_to_char( "Done.\n\r", ch );
  return;
}