/****************************************************************************
 * [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;
}
