eldhamud_2.3.3/clans/
eldhamud_2.3.3/councils/
eldhamud_2.3.3/doc/
eldhamud_2.3.3/doc/DIKU/
eldhamud_2.3.3/doc/DIKU/.svn/
eldhamud_2.3.3/doc/DIKU/.svn/prop-base/
eldhamud_2.3.3/doc/DIKU/.svn/text-base/
eldhamud_2.3.3/doc/MERC/
eldhamud_2.3.3/doc/MERC/.svn/
eldhamud_2.3.3/doc/SMAUG/.svn/
eldhamud_2.3.3/doc/SMAUG/mudprogs/
eldhamud_2.3.3/doc/SMAUG/mudprogs/.svn/
eldhamud_2.3.3/doc/mudprogs/
eldhamud_2.3.3/gods/
eldhamud_2.3.3/houses/
eldhamud_2.3.3/player/a/
/****************************************************************************
 *   Eldhamud Codebase V2.2        *
 * ------------------------------------------------------------------------ *
 *          EldhaMUD code (C) 2003-2008 by Robert Powell (Tommi)            *
 * ------------------------------------------------------------------------ *
 * Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag,        *
 * Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard,        *
 * Grishnakh, Fireblade, and Nivek.                                         *
 *                                                                          *
 * Original MERC 2.1 code by Hatchet, Furey, and Kahn.                      *
 *                                                                          *
 * Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen,      *
 * Michael Seifert, and Sebastian Hammer.                                   *
 * ------------------------------------------------------------------------ *
 *        Player skills module       *
 ****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "./Headers/mud.h"
extern int get_secflag ( char *flag );
ch_ret spell_attack ( int, int, CHAR_DATA *, void * );
char *const spell_flag[] = { "water", "earth", "air", "astral", "area", "distant", "reverse",
                             "noself", "_unused2_", "accumulative", "recastable", "noscribe",
                             "nobrew", "group", "object", "character", "secretskill", "pksensitive",
                             "stoponfail", "nofight", "nodispel", "randomtarget", "r2", "r3", "r4",
                             "r5", "r6", "r7", "r8", "r9", "r10", "r11"
                           };
char *const spell_saves[] = { "none", "poison_death", "wands", "para_petri", "breath", "spell_staff"
                            };
char *const spell_save_effect[] = { "none", "negate", "eightdam", "quarterdam", "halfdam", "3qtrdam",
                                    "reflect", "absorb"
                                  };
char *const spell_damage[] = { "none", "fire", "cold", "electricity", "energy", "acid", "poison", "drain"
                             };
char *const spell_action[] = { "none", "create", "destroy", "resist", "suscept", "divinate", "obscure",
                               "change"
                             };
char *const spell_power[] = { "none", "minor", "greater", "major"
                            };
char *const spell_Class[] = { "none", "lunar", "solar", "travel", "summon", "life", "death", "illusion"
                            };
char *const target_type[] = { "ignore", "offensive", "defensive", "self", "objinv"
                            };
void show_char_to_char ( CHAR_DATA * list, CHAR_DATA * ch );
bool check_illegal_psteal ( CHAR_DATA * ch, CHAR_DATA * victim );
/*
 * Dummy function
 */
void skill_notfound ( CHAR_DATA * ch, char *argument )
{
  send_to_char ( "Huh?\n\r", ch );
  return;
}

int get_ssave ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_saves ) / sizeof ( spell_saves[0] ); x++ )
    if ( !str_cmp ( name, spell_saves[x] ) )
      return x;

  return -1;
}

int get_starget ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( target_type ) / sizeof ( target_type[0] ); x++ )
    if ( !str_cmp ( name, target_type[x] ) )
      return x;

  return -1;
}

int get_sflag ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_flag ) / sizeof ( spell_flag[0] ); x++ )
    if ( !str_cmp ( name, spell_flag[x] ) )
      return x;

  return -1;
}

int get_sdamage ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_damage ) / sizeof ( spell_damage[0] ); x++ )
    if ( !str_cmp ( name, spell_damage[x] ) )
      return x;

  return -1;
}

int get_saction ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_action ) / sizeof ( spell_action[0] ); x++ )
    if ( !str_cmp ( name, spell_action[x] ) )
      return x;

  return -1;
}

int get_ssave_effect ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_save_effect ) / sizeof ( spell_save_effect[0] ); x++ )
    if ( !str_cmp ( name, spell_save_effect[x] ) )
      return x;

  return -1;
}

int get_spower ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_power ) / sizeof ( spell_power[0] ); x++ )
    if ( !str_cmp ( name, spell_power[x] ) )
      return x;

  return -1;
}

int get_sClass ( char *name )
{
  int x;

  for ( x = 0; x < sizeof ( spell_Class ) / sizeof ( spell_Class[0] ); x++ )
    if ( !str_cmp ( name, spell_Class[x] ) )
      return x;

  return -1;
}

bool is_legal_kill ( CHAR_DATA * ch, CHAR_DATA * vch )
{
  if ( IS_NPC ( ch ) || IS_NPC ( vch ) )
    return TRUE;

  if ( !IS_PKILL ( ch ) || !IS_PKILL ( vch ) )
    return FALSE;

  if ( ch->pcdata->clan && ch->pcdata->clan == vch->pcdata->clan )
    return FALSE;

  return TRUE;
}

/*
 * Perform a binary search on a section of the skill table
 * Each different section of the skill table is sorted alphabetically
 * Only match skills player knows    -Thoric
 */
bool check_skill ( CHAR_DATA * ch, char *command, char *argument )
{
  int sn;
  int first = gsn_first_skill;
  int top = gsn_first_weapon - 1;
  int mana, blood;

  struct timeval time_used;
  /*
   * bsearch for the skill
   */

  for ( ;; )
  {
    sn = ( first + top ) >> 1;

    if ( LOWER ( command[0] ) == LOWER ( skill_table[sn]->name[0] )
         && !str_prefix ( command, skill_table[sn]->name ) && ( skill_table[sn]->skill_fun || skill_table[sn]->spell_fun != spell_null ) && ( can_use_skill ( ch, 0, sn ) ) )
      /*
       * &&  (IS_NPC(ch)
       * ||  (ch->pcdata->learned[sn] > 0
       * &&   ch->level >= skill_table[sn]->skill_level[ch->Class])) )
       */
      break;

    if ( first >= top )
      return FALSE;

    if ( strcmp ( command, skill_table[sn]->name ) < 1 )
      top = sn - 1;
    else
      first = sn + 1;
  }

  if ( !check_pos ( ch, skill_table[sn]->minimum_position ) )
    return TRUE;

  if ( IS_NPC ( ch ) && ( IS_AFFECTED ( ch, AFF_CHARM ) || IS_AFFECTED ( ch, AFF_POSSESS ) ) )
  {
    send_to_char ( "For some reason, you seem unable to perform that...\n\r", ch );
    act ( AT_GREY, "$n wanders around aimlessly.", ch, NULL, NULL, TO_ROOM );
    return TRUE;
  }

  /*
   * check if mana is required
   */
  if ( skill_table[sn]->min_mana )
  {
    mana = IS_NPC ( ch ) ? 0 : UMAX ( skill_table[sn]->min_mana, 100 / ( 2 + ch->level - skill_table[sn]->skill_level[ch->Class] ) );
    blood = UMAX ( 1, ( mana + 4 ) / 8 );  /* NPCs don't have PCDatas. -- Altrag */

    if ( !IS_NPC ( ch ) && ch->mana < mana )
    {
      send_to_char ( "You don't have enough mana.\n\r", ch );
      return TRUE;
    }
  }
  else
  {
    mana = 0;
    blood = 0;
  }

  /*
   * Is this a real do-fun, or a really a spell?
   */
  if ( !skill_table[sn]->skill_fun )
  {
    ch_ret retcode = rNONE;
    void *vo = NULL;
    CHAR_DATA *victim = NULL;
    OBJ_DATA *obj = NULL;
    target_name = "";

    switch ( skill_table[sn]->target )
    {

      default:
        bug ( "Check_skill: bad target for sn %d.", sn );
        send_to_char ( "Something went wrong...\n\r", ch );
        return TRUE;

      case TAR_IGNORE:
        vo = NULL;

        if ( argument[0] == STRING_NULL )
        {
          if ( ( victim = who_fighting ( ch ) ) != NULL )
            target_name = victim->name;
        }
        else
          target_name = argument;

        break;

      case TAR_CHAR_OFFENSIVE:
        {
          if ( argument[0] == STRING_NULL && ( victim = who_fighting ( ch ) ) == NULL )
          {
            ch_printf ( ch, "Confusion overcomes you as your '%s' has no target.\n\r", skill_table[sn]->name );
            return TRUE;
          }
          else
            if ( argument[0] != STRING_NULL && ( victim = get_char_room ( ch, argument ) ) == NULL )
            {
              send_to_char ( "They aren't here.\n\r", ch );
              return TRUE;
            }
        }

        if ( is_safe ( ch, victim, TRUE ) )
          return TRUE;

        if ( ch == victim && SPELL_FLAG ( skill_table[sn], SF_NOSELF ) )
        {
          send_to_char ( "You can't target yourself!\n\r", ch );
          return TRUE;
        }

        if ( !IS_NPC ( ch ) )
        {
          if ( !IS_NPC ( victim ) )
          {
            /*
             * Sheesh! can't do anything
             * send_to_char( "You can't do that on a player.\n\r", ch );
             * return TRUE;
             */
            /*
             * if ( xIS_SET(victim->act, PLR_PK))
             */
            if ( get_timer ( ch, TIMER_PKILLED ) > 0 )
            {
              send_to_char ( "You have been killed in the last 5 minutes.\n\r", ch );
              return TRUE;
            }

            if ( get_timer ( victim, TIMER_PKILLED ) > 0 )
            {
              send_to_char ( "This player has been killed in the last 5 minutes.\n\r", ch );
              return TRUE;
            }

            if ( victim != ch )
              send_to_char ( "You really shouldn't do this to another player...\n\r", ch );
          }

          if ( IS_AFFECTED ( ch, AFF_CHARM ) && ch->master == victim )
          {
            send_to_char ( "You can't do that on your own follower.\n\r", ch );
            return TRUE;
          }
        }

        check_illegal_pk ( ch, victim );

        vo = ( void * ) victim;
        break;

      case TAR_CHAR_DEFENSIVE:
        {
          if ( argument[0] != STRING_NULL && ( victim = get_char_room ( ch, argument ) ) == NULL )
          {
            send_to_char ( "They aren't here.\n\r", ch );
            return TRUE;
          }

          if ( !victim )
            victim = ch;
        }

        if ( ch == victim && SPELL_FLAG ( skill_table[sn], SF_NOSELF ) )
        {
          send_to_char ( "You can't target yourself!\n\r", ch );
          return TRUE;
        }

        vo = ( void * ) victim;

        break;

      case TAR_CHAR_SELF:
        victim = ch;
        break;

      case TAR_OBJ_INV:
        {
          if ( ( obj = get_obj_carry ( ch, argument ) ) == NULL )
          {
            send_to_char ( "You can't find that.\n\r", ch );
            return TRUE;
          }
        }

        vo = ( void * ) obj;
        break;
    }

    /*
     * waitstate
     */
    WAIT_STATE ( ch, skill_table[sn]->beats );

    /*
     * check for failure
     */
    if ( ( number_percent(  ) + skill_table[sn]->difficulty * 5 ) > ( IS_NPC ( ch ) ? 75 : LEARNED ( ch, sn ) ) )
    {
      failed_casting ( skill_table[sn], ch, victim, obj );
      learn_from_failure ( ch, sn );

      if ( mana )
      {
        ch->mana -= mana / 2;
      }

      return TRUE;
    }

    if ( mana )
    {
      ch->mana -= mana;
    }

    start_timer ( &time_used );

    retcode = ( *skill_table[sn]->spell_fun ) ( sn, ch->level, ch, vo );
    end_timer ( &time_used );
    update_userec ( &time_used, &skill_table[sn]->userec );

    if ( retcode == rCHAR_DIED || retcode == rERROR )
      return TRUE;

    if ( char_died ( ch ) )
      return TRUE;

    if ( retcode == rSPELL_FAILED )
    {
      learn_from_failure ( ch, sn );
      retcode = rNONE;
    }
    else
      learn_from_success ( ch, sn );

    if ( skill_table[sn]->target == TAR_CHAR_OFFENSIVE && victim != ch && !char_died ( victim ) )
    {
      CHAR_DATA *vch;
      CHAR_DATA *vch_next;

      for ( vch = ch->in_room->first_person; vch; vch = vch_next )
      {
        vch_next = vch->next_in_room;

        if ( victim == vch && !victim->fighting && victim->master != ch )
        {
          retcode = multi_hit ( victim, ch, TYPE_UNDEFINED );
          break;
        }
      }
    }

    return TRUE;
  }

  if ( mana )
  {
    ch->mana -= mana;
  }

  ch->prev_cmd = ch->last_cmd;  /* haus, for automapping */

  ch->last_cmd = skill_table[sn]->skill_fun;
  start_timer ( &time_used );
  ( *skill_table[sn]->skill_fun ) ( ch, argument );
  end_timer ( &time_used );
  update_userec ( &time_used, &skill_table[sn]->userec );
  tail_chain(  );
  return TRUE;
}

void do_skin ( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *korps;
  OBJ_DATA *corpse;
  OBJ_DATA *obj;
  OBJ_DATA *skin;
  bool found;
  char *name;
  char buf[MAX_STRING_LENGTH];
  found = FALSE;

  if ( !IS_PKILL ( ch ) && !IS_IMMORTAL ( ch ) )
  {
    send_to_char ( "Leave the hideous defilings to the killers!\n", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    send_to_char ( "Whose corpse do you wish to skin?\n\r", ch );
    return;
  }

  if ( ( corpse = get_obj_here ( ch, argument ) ) == NULL )
  {
    send_to_char ( "You cannot find that here.\n\r", ch );
    return;
  }

  if ( ( obj = get_eq_char ( ch, WEAR_WIELD ) ) == NULL )
  {
    send_to_char ( "You have no weapon with which to perform this deed.\n\r", ch );
    return;
  }

  if ( corpse->item_type != ITEM_CORPSE_NPC )
  {
    send_to_char ( "You can only skin the bodies of player characters.\n\r", ch );
    return;
  }

  /*
   * if ( obj->value[3] != 1
   * &&   obj->value[3] != 2
   * &&   obj->value[3] != 3
   * &&   obj->value[3] != 11 )
   * {
   * send_to_char( "There is nothing you can do with this corpse.\n\r", ch );
   * return;
   * }
   */
  if ( get_obj_index ( OBJ_VNUM_SKIN ) == NULL )
  {
    bug ( "Vnum 23 (OBJ_VNUM_SKIN) not found for do_skin!", 0 );
    return;
  }

  korps = create_object ( get_obj_index ( OBJ_VNUM_CORPSE_PC ), 0 );

  skin = create_object ( get_obj_index ( OBJ_VNUM_SKIN ), 0 );
  name = IS_NPC ( ch ) ? korps->short_descr : corpse->short_descr;
  sprintf ( buf, skin->short_descr, name );
  STRFREE ( skin->short_descr );
  skin->short_descr = STRALLOC ( buf );
  sprintf ( buf, skin->description, name );
  STRFREE ( skin->description );
  skin->description = STRALLOC ( buf );
  act ( AT_BLOOD, "$n strips the skin from $p.", ch, corpse, NULL, TO_ROOM );
  act ( AT_BLOOD, "You strip the skin from $p.", ch, corpse, NULL, TO_CHAR );
  /*
   * act( AT_MAGIC, "\nThe skinless corpse is dragged through the ground by a strange force...", ch, corpse, NULL, TO_CHAR);
   * act( AT_MAGIC, "\nThe skinless corpse is dragged through the ground by a strange force...", ch, corpse, NULL, TO_ROOM);
   */
  extract_obj ( corpse );
  obj_to_char ( skin, ch );
  return;
}

/*
 * Lookup a skills information
 * High god command
 */
void do_slookup ( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_INPUT_LENGTH];
  int sn;
  int iClass, iRace;
  SKILLTYPE *skill = NULL;
  one_argument ( argument, arg );

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Slookup what?\n\r", ch );
    return;
  }

  if ( !str_cmp ( arg, "all" ) )
  {
    for ( sn = 0; sn < top_sn && skill_table[sn] && skill_table[sn]->name; sn++ )
      pager_printf ( ch, "Sn: %4d Slot: %4d Skill/spell: '%-20s' Damtype: %s\n\r",
                     sn, skill_table[sn]->slot, skill_table[sn]->name, spell_damage[SPELL_DAMAGE ( skill_table[sn] ) ] );
  }
  else
    if ( !str_cmp ( arg, "herbs" ) )
    {
      for ( sn = 0; sn < top_herb && herb_table[sn] && herb_table[sn]->name; sn++ )
        pager_printf ( ch, "%d) %s\n\r", sn, herb_table[sn]->name );
    }
    else
    {
      SMAUG_AFF *aff;
      int cnt = 0;

      if ( arg[0] == 'h' && is_number ( arg + 1 ) )
      {
        sn = atoi ( arg + 1 );

        if ( !IS_VALID_HERB ( sn ) )
        {
          send_to_char ( "Invalid herb.\n\r", ch );
          return;
        }

        skill = herb_table[sn];
      }
      else
        if ( is_number ( arg ) )
        {
          sn = atoi ( arg );

          if ( ( skill = get_skilltype ( sn ) ) == NULL )
          {
            send_to_char ( "Invalid sn.\n\r", ch );
            return;
          }

          sn %= 1000;
        }
        else
          if ( ( sn = skill_lookup ( arg ) ) >= 0 )
            skill = skill_table[sn];
          else
            if ( ( sn = herb_lookup ( arg ) ) >= 0 )
              skill = herb_table[sn];
            else
            {
              send_to_char ( "No such skill, spell, proficiency or tongue.\n\r", ch );
              return;
            }

      if ( !skill )
      {
        send_to_char ( "Not created yet.\n\r", ch );
        return;
      }

      ch_printf ( ch, "Sn: %4d Slot: %4d %s: '%-20s'\n\r", sn, skill->slot, skill_tname[skill->type], skill->name );

      if ( skill->info )
        ch_printf ( ch, "DamType: %s  ActType: %s   ClassType: %s   PowerType: %s\n\r",
                    spell_damage[SPELL_DAMAGE ( skill ) ], spell_action[SPELL_ACTION ( skill ) ], spell_Class[SPELL_CLASS ( skill ) ], spell_power[SPELL_POWER ( skill ) ] );

      if ( skill->flags )
      {
        int x;
        strcpy ( buf, "Flags:" );

        for ( x = 0; x < 32; x++ )
          if ( SPELL_FLAG ( skill, 1 << x ) )
          {
            strcat ( buf, " " );
            strcat ( buf, spell_flag[x] );
          }

        strcat ( buf, "\n\r" );

        send_to_char ( buf, ch );
      }

      ch_printf ( ch, "Saves: %s  SaveEffect: %s\n\r", spell_saves[ ( int ) skill->saves], spell_save_effect[SPELL_SAVE ( skill ) ] );

      if ( skill->difficulty != STRING_NULL )
        ch_printf ( ch, "Difficulty: %d\n\r", ( int ) skill->difficulty );

      ch_printf ( ch, "Type: %s  Target: %s  Minpos: %d  Mana: %d  Beats: %d  Range: %d\n\r",
                  skill_tname[skill->type],
                  target_type[URANGE ( TAR_IGNORE, skill->target, TAR_OBJ_INV ) ], skill->minimum_position, skill->min_mana, skill->beats, skill->range );

      ch_printf ( ch, "Flags: %d  Guild: %d  Value: %d  Info: %d  Code: %s\n\r",
                  skill->flags, skill->guild, skill->value, skill->info, skill->skill_fun ? skill->skill_fun_name : skill->spell_fun_name );

      ch_printf ( ch, "Difficulty: %d \n\r", skill->difficulty );

      ch_printf ( ch, "Sectors Allowed: %s\n", skill->spell_sector ? flag_string ( skill->spell_sector, sec_flags ) : "All" );

      ch_printf ( ch, "Dammsg: %s\n\rWearoff: %s\n", skill->noun_damage, skill->msg_off ? skill->msg_off : "(none set)" );

      if ( skill->dice && skill->dice[0] != STRING_NULL )
        ch_printf ( ch, "Dice: %s\n\r", skill->dice );

      if ( skill->teachers && skill->teachers[0] != STRING_NULL )
        ch_printf ( ch, "Teachers: %s\n\r", skill->teachers );

      if ( skill->components && skill->components[0] != STRING_NULL )
        ch_printf ( ch, "Components: %s\n\r", skill->components );

      if ( skill->participants )
        ch_printf ( ch, "Participants: %d\n\r", ( int ) skill->participants );

      if ( skill->userec.num_uses )
        send_timer ( &skill->userec, ch );

      for ( aff = skill->affects; aff; aff = aff->next )
      {
        if ( aff == skill->affects )
          send_to_char ( "\n\r", ch );

        sprintf ( buf, "Affect %d", ++cnt );

        if ( aff->location )
        {
          strcat ( buf, " modifies " );
          strcat ( buf, a_types[aff->location % REVERSE_APPLY] );
          strcat ( buf, " by '" );
          strcat ( buf, aff->modifier );

          if ( aff->bitvector != -1 )
            strcat ( buf, "' and" );
          else
            strcat ( buf, "'" );
        }

        if ( aff->bitvector != -1 )
        {
          strcat ( buf, " applies " );
          strcat ( buf, a_flags[aff->bitvector] );
        }

        if ( aff->duration[0] != STRING_NULL && aff->duration[0] != '0' )
        {
          strcat ( buf, " for '" );
          strcat ( buf, aff->duration );
          strcat ( buf, "' rounds" );
        }

        if ( aff->location >= REVERSE_APPLY )
          strcat ( buf, " (affects caster only)" );

        strcat ( buf, "\n\r" );

        send_to_char ( buf, ch );

        if ( !aff->next )
          send_to_char ( "\n\r", ch );
      }

      if ( skill->hit_char && skill->hit_char[0] != STRING_NULL )
        ch_printf ( ch, "Hitchar   : %s\n\r", skill->hit_char );

      if ( skill->hit_vict && skill->hit_vict[0] != STRING_NULL )
        ch_printf ( ch, "Hitvict   : %s\n\r", skill->hit_vict );

      if ( skill->hit_room && skill->hit_room[0] != STRING_NULL )
        ch_printf ( ch, "Hitroom   : %s\n\r", skill->hit_room );

      if ( skill->hit_dest && skill->hit_dest[0] != STRING_NULL )
        ch_printf ( ch, "Hitdest   : %s\n\r", skill->hit_dest );

      if ( skill->miss_char && skill->miss_char[0] != STRING_NULL )
        ch_printf ( ch, "Misschar  : %s\n\r", skill->miss_char );

      if ( skill->miss_vict && skill->miss_vict[0] != STRING_NULL )
        ch_printf ( ch, "Missvict  : %s\n\r", skill->miss_vict );

      if ( skill->miss_room && skill->miss_room[0] != STRING_NULL )
        ch_printf ( ch, "Missroom  : %s\n\r", skill->miss_room );

      if ( skill->die_char && skill->die_char[0] != STRING_NULL )
        ch_printf ( ch, "Diechar   : %s\n\r", skill->die_char );

      if ( skill->die_vict && skill->die_vict[0] != STRING_NULL )
        ch_printf ( ch, "Dievict   : %s\n\r", skill->die_vict );

      if ( skill->die_room && skill->die_room[0] != STRING_NULL )
        ch_printf ( ch, "Dieroom   : %s\n\r", skill->die_room );

      if ( skill->imm_char && skill->imm_char[0] != STRING_NULL )
        ch_printf ( ch, "Immchar   : %s\n\r", skill->imm_char );

      if ( skill->imm_vict && skill->imm_vict[0] != STRING_NULL )
        ch_printf ( ch, "Immvict   : %s\n\r", skill->imm_vict );

      if ( skill->imm_room && skill->imm_room[0] != STRING_NULL )
        ch_printf ( ch, "Immroom   : %s\n\r", skill->imm_room );

      if ( skill->type != SKILL_HERB )
      {
        if ( skill->type != SKILL_RACIAL )
        {
          send_to_char ( "--------------------------[CLASS USE]--------------------------\n\r", ch );

          for ( iClass = 0; iClass < MAX_PC_CLASS; iClass++ )
          {
            strcpy ( buf, Class_table[iClass]->who_name );
            sprintf ( buf + 3, ") lvl: %3d max: %2d%%", skill->skill_level[iClass], skill->skill_adept[iClass] );

            if ( iClass % 3 == 2 )
              strcat ( buf, "\n\r" );
            else
              strcat ( buf, "  " );

            send_to_char ( buf, ch );
          }
        }
        else
        {
          send_to_char ( "\n\r--------------------------[RACE USE]--------------------------\n\r", ch );

          for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
          {
            sprintf ( buf, "%8.8s) lvl: %3d max: %2d%%", race_table[iRace]->race_name, skill->race_level[iRace], skill->race_adept[iRace] );

            if ( !strcmp ( race_table[iRace]->race_name, "unused" ) )
              sprintf ( buf, "                           " );

            if ( ( iRace > 0 ) && ( iRace % 2 == 1 ) )
              strcat ( buf, "\n\r" );
            else
              strcat ( buf, "  " );

            send_to_char ( buf, ch );
          }
        }
      }

      send_to_char ( "\n\r", ch );
    }

  return;
}

/*
 * Set a skill's attributes or what skills a player has.
 * High god command, with support for creating skills/spells/herbs/etc
 */
void do_sset ( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  int value;
  int sn, i;
  bool fAll;
  argument = one_argument ( argument, arg1 );
  argument = one_argument ( argument, arg2 );

  if ( arg1[0] == STRING_NULL || arg2[0] == STRING_NULL || argument[0] == STRING_NULL )
  {
    send_to_char ( "Syntax: sset <victim> <skill> <value>\n\r", ch );
    send_to_char ( "or:     sset <victim> all     <value>\n\r", ch );

    if ( get_trust ( ch ) > LEVEL_SUB_IMPLEM )
    {
      send_to_char ( "or:     sset save skill table\n\r", ch );
      send_to_char ( "or:     sset save herb table\n\r", ch );
      send_to_char ( "or:     sset create skill 'new skill'\n\r", ch );
      send_to_char ( "or:     sset create herb 'new herb'\n\r", ch );
      send_to_char ( "or:     sset create ability 'new ability'\n\r", ch );
    }

    if ( get_trust ( ch ) > LEVEL_GREATER )
    {
      send_to_char ( "or:     sset <sn>     <field> <value>\n\r", ch );
      send_to_char ( "\n\rField being one of:\n\r", ch );
      send_to_char ( "  name code target minpos slot mana beats dammsg wearoff guild minlevel\n\r", ch );
      send_to_char ( "  type damtype acttype Classtype powertype seffect flag dice value difficulty\n\r", ch );
      send_to_char ( "  affect rmaffect level adept hit miss die imm (char/vict/room)\n\r", ch );
      send_to_char ( "  components teachers racelevel raceadept\n\r", ch );
      send_to_char ( "  sector\n\r", ch );
      send_to_char ( "Affect having the fields: <location> <modfifier> [duration] [bitvector]\n\r", ch );
      send_to_char ( "(See AFFECTTYPES for location, and AFFECTED_BY for bitvector)\n\r", ch );
    }

    send_to_char ( "Skill being any skill or spell.\n\r", ch );

    return;
  }

  if ( get_trust ( ch ) > LEVEL_SUB_IMPLEM && !str_cmp ( arg1, "save" ) && !str_cmp ( argument, "table" ) )
  {
    if ( !str_cmp ( arg2, "skill" ) )
    {
      send_to_char ( "Saving skill table...\n\r", ch );
      save_skill_table(  );
      save_Classes(  );
      /*
       * save_races();
       */
      return;
    }

    if ( !str_cmp ( arg2, "herb" ) )
    {
      send_to_char ( "Saving herb table...\n\r", ch );
      save_herb_table(  );
      return;
    }
  }

  if ( get_trust ( ch ) > LEVEL_SUB_IMPLEM && !str_cmp ( arg1, "create" ) && ( !str_cmp ( arg2, "skill" ) || !str_cmp ( arg2, "herb" ) || !str_cmp ( arg2, "ability" ) ) )
  {

    struct skill_type *skill;
    short type = SKILL_UNKNOWN;

    if ( !str_cmp ( arg2, "herb" ) )
    {
      type = SKILL_HERB;

      if ( top_herb >= MAX_HERB )
      {
        ch_printf ( ch, "The current top herb is %d, which is the maximum.  "
                    "To add more herbs,\n\rMAX_HERB will have to be " "raised in mud.h, and the mud recompiled.\n\r", top_herb );
        return;
      }
    }
    else
      if ( top_sn >= MAX_SKILL )
      {
        ch_printf ( ch, "The current top sn is %d, which is the maximum.  "
                    "To add more skills,\n\rMAX_SKILL will have to be " "raised in mud.h, and the mud recompiled.\n\r", top_sn );
        return;
      }

    CREATE ( skill, struct skill_type, 1 );

    skill->slot = 0;

    if ( type == SKILL_HERB )
    {
      int max, x;
      herb_table[top_herb++] = skill;

      for ( max = x = 0; x < top_herb - 1; x++ )
        if ( herb_table[x] && herb_table[x]->slot > max )
          max = herb_table[x]->slot;

      skill->slot = max + 1;
    }
    else
      skill_table[top_sn++] = skill;

    skill->min_mana = 0;

    skill->name = str_dup ( argument );

    skill->noun_damage = str_dup ( "" );

    skill->msg_off = str_dup ( "" );

    skill->spell_fun = spell_smaug;

    skill->type = type;

    skill->spell_sector = 0;

    skill->guild = -1;

    if ( !str_cmp ( arg2, "ability" ) )
      skill->type = SKILL_RACIAL;

    for ( i = 0; i < MAX_PC_CLASS; i++ )
    {
      skill->skill_level[i] = LEVEL_IMMORTAL;
      skill->skill_adept[i] = 95;
    }

    for ( i = 0; i < MAX_PC_RACE; i++ )
    {
      skill->race_level[i] = LEVEL_IMMORTAL;
      skill->race_adept[i] = 95;
    }

    send_to_char ( "Done.\n\r", ch );

    return;
  }

  if ( arg1[0] == 'h' )
    sn = atoi ( arg1 + 1 );
  else
    sn = atoi ( arg1 );

  if ( get_trust ( ch ) > LEVEL_GREATER
       && ( ( arg1[0] == 'h' && is_number ( arg1 + 1 ) && ( sn = atoi ( arg1 + 1 ) ) >= 0 ) || ( is_number ( arg1 ) && ( sn = atoi ( arg1 ) ) >= 0 ) ) )
  {

    struct skill_type *skill;

    if ( arg1[0] == 'h' )
    {
      if ( sn >= top_herb )
      {
        send_to_char ( "Herb number out of range.\n\r", ch );
        return;
      }

      skill = herb_table[sn];
    }
    else
    {
      if ( ( skill = get_skilltype ( sn ) ) == NULL )
      {
        send_to_char ( "Skill number out of range.\n\r", ch );
        return;
      }

      sn %= 1000;
    }

    if ( !str_cmp ( arg2, "difficulty" ) )
    {
      skill->difficulty = atoi ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "participants" ) )
    {
      skill->participants = atoi ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "damtype" ) )
    {
      int x = get_sdamage ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell damage type.\n\r", ch );
      else
      {
        SET_SDAM ( skill, x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "acttype" ) )
    {
      int x = get_saction ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell action type.\n\r", ch );
      else
      {
        SET_SACT ( skill, x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "Classtype" ) )
    {
      int x = get_sClass ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell Class type.\n\r", ch );
      else
      {
        SET_SCLA ( skill, x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "powertype" ) )
    {
      int x = get_spower ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell power type.\n\r", ch );
      else
      {
        SET_SPOW ( skill, x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "seffect" ) )
    {
      int x = get_ssave_effect ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell save effect type.\n\r", ch );
      else
      {
        SET_SSAV ( skill, x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "flag" ) )
    {
      int x = get_sflag ( argument );

      if ( x == -1 )
        send_to_char ( "Not a spell flag.\n\r", ch );
      else
      {
        TOGGLE_BIT ( skill->flags, 1 << x );
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "saves" ) )
    {
      int x = get_ssave ( argument );

      if ( x == -1 )
        send_to_char ( "Not a saving type.\n\r", ch );
      else
      {
        skill->saves = x;
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "code" ) )
    {
      SPELL_FUN *spellfun;
      DO_FUN *dofun;

      if ( !str_prefix ( "do_", argument ) && ( dofun = skill_function ( argument ) ) != skill_notfound )
      {
        skill->skill_fun = dofun;
        skill->spell_fun = NULL;
        DISPOSE ( skill->skill_fun_name );
        skill->skill_fun_name = str_dup ( argument );
      }
      else
        if ( ( spellfun = spell_function ( argument ) ) != spell_notfound )
        {
          skill->spell_fun = spellfun;
          skill->skill_fun = NULL;
          DISPOSE ( skill->skill_fun_name );
          skill->spell_fun_name = str_dup ( argument );
        }
        else
        {
          send_to_char ( "Not a spell or skill.\n\r", ch );
          return;
        }

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "target" ) )
    {
      int x = get_starget ( argument );

      if ( x == -1 )
        send_to_char ( "Not a valid target type.\n\r", ch );
      else
      {
        skill->target = x;
        send_to_char ( "Ok.\n\r", ch );
      }

      return;
    }

    if ( !str_cmp ( arg2, "minpos" ) )
    {
      skill->minimum_position = URANGE ( POS_DEAD, atoi ( argument ), POS_DRAG );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "minlevel" ) )
    {
      skill->min_level = URANGE ( 1, atoi ( argument ), MAX_LEVEL );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "sector" ) )
    {
      char tmp_arg[MAX_STRING_LENGTH];

      while ( argument[0] != STRING_NULL )
      {
        argument = one_argument ( argument, tmp_arg );
        value = get_secflag ( tmp_arg );

        if ( value < 0 || value > SECT_MAX )
          ch_printf ( ch, "Unknown flag: %s\n\r", tmp_arg );
        else
          TOGGLE_BIT ( skill->spell_sector, ( 1 << value ) );
      }

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "slot" ) )
    {
      skill->slot = URANGE ( 0, atoi ( argument ), 30000 );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "mana" ) )
    {
      skill->min_mana = URANGE ( 0, atoi ( argument ), 2000 );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "beats" ) )
    {
      skill->beats = URANGE ( 0, atoi ( argument ), 120 );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "range" ) )
    {
      skill->range = URANGE ( 0, atoi ( argument ), 20 );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "guild" ) )
    {
      skill->guild = atoi ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "value" ) )
    {
      skill->value = atoi ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "type" ) )
    {
      skill->type = get_skill ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "rmaffect" ) )
    {
      SMAUG_AFF *aff = skill->affects;
      SMAUG_AFF *aff_next;
      int num = atoi ( argument );
      int cnt = 1;

      if ( !aff )
      {
        send_to_char ( "This spell has no special affects to remove.\n\r", ch );
        return;
      }

      if ( num == 1 )
      {
        skill->affects = aff->next;
        DISPOSE ( aff->duration );
        DISPOSE ( aff->modifier );
        DISPOSE ( aff );
        send_to_char ( "Removed.\n\r", ch );
        return;
      }

      for ( ; aff; aff = aff->next )
      {
        if ( ++cnt == num && ( aff_next = aff->next ) != NULL )
        {
          aff->next = aff_next->next;
          DISPOSE ( aff_next->duration );
          DISPOSE ( aff_next->modifier );
          DISPOSE ( aff_next );
          send_to_char ( "Removed.\n\r", ch );
          return;
        }
      }

      send_to_char ( "Not found.\n\r", ch );

      return;
    }

    /*
     * affect <location> <modifier> <duration> <bitvector>
     */
    if ( !str_cmp ( arg2, "affect" ) )
    {
      char location[MAX_INPUT_LENGTH];
      char modifier[MAX_INPUT_LENGTH];
      char duration[MAX_INPUT_LENGTH];
      /*
       * char bitvector[MAX_INPUT_LENGTH];
       */
      int loc, bit, tmpbit;
      SMAUG_AFF *aff;
      argument = one_argument ( argument, location );
      argument = one_argument ( argument, modifier );
      argument = one_argument ( argument, duration );

      if ( location[0] == '!' )
        loc = get_atype ( location + 1 ) + REVERSE_APPLY;
      else
        loc = get_atype ( location );

      if ( ( loc % REVERSE_APPLY ) < 0 || ( loc % REVERSE_APPLY ) >= MAX_APPLY_TYPE )
      {
        send_to_char ( "Unknown affect location.  See AFFECTTYPES.\n\r", ch );
        return;
      }

      bit = -1;

      if ( argument[0] != STRING_NULL )
      {
        if ( ( tmpbit = get_aflag ( argument ) ) == -1 )
          ch_printf ( ch, "Unknown bitvector: %s.  See AFFECTED_BY\n\r", argument );
        else
          bit = tmpbit;
      }

      CREATE ( aff, SMAUG_AFF, 1 );

      if ( !str_cmp ( duration, "0" ) )
        duration[0] = STRING_NULL;

      if ( !str_cmp ( modifier, "0" ) )
        modifier[0] = STRING_NULL;

      aff->duration = str_dup ( duration );

      aff->location = loc;

      if ( loc == APPLY_AFFECT || loc == APPLY_RESISTANT || loc == APPLY_IMMUNE || loc == APPLY_SUSCEPTIBLE )
      {
        int modval = get_aflag ( modifier );
        /*
         * Sanitize the flag input for the modifier if needed -- Samson
         */

        if ( modval < 0 )
          modval = 0;

        snprintf ( modifier, MAX_INPUT_LENGTH, "%d", modval );
      }

      aff->modifier = str_dup ( modifier );

      aff->bitvector = bit;
      aff->next = skill->affects;
      skill->affects = aff;
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "level" ) )
    {
      char arg3[MAX_INPUT_LENGTH];
      int Class;
      argument = one_argument ( argument, arg3 );
      Class = atoi ( arg3 );

      if ( Class >= MAX_PC_CLASS || Class < 0 )
        send_to_char ( "Not a valid Class.\n\r", ch );
      else
        skill->skill_level[Class] = URANGE ( 0, atoi ( argument ), MAX_LEVEL );

      return;
    }

    if ( !str_cmp ( arg2, "racelevel" ) )
    {
      char arg3[MAX_INPUT_LENGTH];
      int race;
      argument = one_argument ( argument, arg3 );
      race = atoi ( arg3 );

      if ( race >= MAX_PC_RACE || race < 0 )
        send_to_char ( "Not a valid race.\n\r", ch );
      else
        skill->race_level[race] = URANGE ( 0, atoi ( argument ), MAX_LEVEL );

      return;
    }

    if ( !str_cmp ( arg2, "adept" ) )
    {
      char arg3[MAX_INPUT_LENGTH];
      int Class;
      argument = one_argument ( argument, arg3 );
      Class = atoi ( arg3 );

      if ( Class >= MAX_PC_CLASS || Class < 0 )
        send_to_char ( "Not a valid Class.\n\r", ch );
      else
        skill->skill_adept[Class] = URANGE ( 0, atoi ( argument ), 100 );

      return;
    }

    if ( !str_cmp ( arg2, "raceadept" ) )
    {
      char arg3[MAX_INPUT_LENGTH];
      int race;
      argument = one_argument ( argument, arg3 );
      race = atoi ( arg3 );

      if ( race >= MAX_PC_RACE || race < 0 )
        send_to_char ( "Not a valid race.\n\r", ch );
      else
        skill->race_adept[race] = URANGE ( 0, atoi ( argument ), 100 );

      return;
    }

    if ( !str_cmp ( arg2, "name" ) )
    {
      DISPOSE ( skill->name );
      skill->name = str_dup ( argument );
      send_to_char ( "Ok.\n\r", ch );
      return;
    }

    if ( !str_cmp ( arg2, "dammsg" ) )
    {
      DISPOSE ( skill->noun_damage );

      if ( !str_cmp ( argument, "clear" ) )
        skill->noun_damage = str_dup ( "" );
      else
        skill->noun_damage = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "wearoff" ) )
    {
      DISPOSE ( skill->msg_off );

      if ( str_cmp ( argument, "clear" ) )
        skill->msg_off = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "hitchar" ) )
    {
      if ( skill->hit_char )
        DISPOSE ( skill->hit_char );

      if ( str_cmp ( argument, "clear" ) )
        skill->hit_char = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "hitvict" ) )
    {
      if ( skill->hit_vict )
        DISPOSE ( skill->hit_vict );

      if ( str_cmp ( argument, "clear" ) )
        skill->hit_vict = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "hitroom" ) )
    {
      if ( skill->hit_room )
        DISPOSE ( skill->hit_room );

      if ( str_cmp ( argument, "clear" ) )
        skill->hit_room = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "hitdest" ) )
    {
      if ( skill->hit_dest )
        DISPOSE ( skill->hit_dest );

      if ( str_cmp ( argument, "clear" ) )
        skill->hit_dest = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "misschar" ) )
    {
      if ( skill->miss_char )
        DISPOSE ( skill->miss_char );

      if ( str_cmp ( argument, "clear" ) )
        skill->miss_char = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "missvict" ) )
    {
      if ( skill->miss_vict )
        DISPOSE ( skill->miss_vict );

      if ( str_cmp ( argument, "clear" ) )
        skill->miss_vict = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "missroom" ) )
    {
      if ( skill->miss_room )
        DISPOSE ( skill->miss_room );

      if ( str_cmp ( argument, "clear" ) )
        skill->miss_room = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "diechar" ) )
    {
      if ( skill->die_char )
        DISPOSE ( skill->die_char );

      if ( str_cmp ( argument, "clear" ) )
        skill->die_char = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "dievict" ) )
    {
      if ( skill->die_vict )
        DISPOSE ( skill->die_vict );

      if ( str_cmp ( argument, "clear" ) )
        skill->die_vict = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "dieroom" ) )
    {
      if ( skill->die_room )
        DISPOSE ( skill->die_room );

      if ( str_cmp ( argument, "clear" ) )
        skill->die_room = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "immchar" ) )
    {
      if ( skill->imm_char )
        DISPOSE ( skill->imm_char );

      if ( str_cmp ( argument, "clear" ) )
        skill->imm_char = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "immvict" ) )
    {
      if ( skill->imm_vict )
        DISPOSE ( skill->imm_vict );

      if ( str_cmp ( argument, "clear" ) )
        skill->imm_vict = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "immroom" ) )
    {
      if ( skill->imm_room )
        DISPOSE ( skill->imm_room );

      if ( str_cmp ( argument, "clear" ) )
        skill->imm_room = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "dice" ) )
    {
      if ( skill->dice )
        DISPOSE ( skill->dice );

      if ( str_cmp ( argument, "clear" ) )
        skill->dice = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "components" ) )
    {
      if ( skill->components )
        DISPOSE ( skill->components );

      if ( str_cmp ( argument, "clear" ) )
        skill->components = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    if ( !str_cmp ( arg2, "teachers" ) )
    {
      if ( skill->teachers )
        DISPOSE ( skill->teachers );

      if ( str_cmp ( argument, "clear" ) )
        skill->teachers = str_dup ( argument );

      send_to_char ( "Ok.\n\r", ch );

      return;
    }

    do_sset ( ch, "" );

    return;
  }

  if ( ( victim = get_char_world ( ch, arg1 ) ) == NULL )
  {
    if ( ( sn = skill_lookup ( arg1 ) ) >= 0 )
    {
      sprintf ( arg1, "%d %s %s", sn, arg2, argument );
      do_sset ( ch, arg1 );
    }
    else
      send_to_char ( "They aren't here.\n\r", ch );

    return;
  }

  if ( IS_NPC ( victim ) )
  {
    send_to_char ( "Not on NPC's.\n\r", ch );
    return;
  }

  fAll = !str_cmp ( arg2, "all" );

  sn = 0;

  if ( !fAll && ( sn = skill_lookup ( arg2 ) ) < 0 )
  {
    send_to_char ( "No such skill or spell.\n\r", ch );
    return;
  }

  /*
   * Snarf the value.
   */
  if ( !is_number ( argument ) )
  {
    send_to_char ( "Value must be numeric.\n\r", ch );
    return;
  }

  value = atoi ( argument );

  if ( value < 0 || value > 100 )
  {
    send_to_char ( "Value range is 0 to 100.\n\r", ch );
    return;
  }

  if ( fAll )
  {
    for ( sn = 0; sn < top_sn; sn++ )
    {
      /*
       * Fix by Narn to prevent ssetting skills the player shouldn't have.
       */
      if ( skill_table[sn]->name && ( victim->level >= skill_table[sn]->skill_level[victim->Class] || value == 0 ) )
      {
        if ( value == 100 && !IS_IMMORTAL ( victim ) )
          victim->pcdata->learned[sn] = GET_ADEPT ( victim, sn );
        else
          victim->pcdata->learned[sn] = value;
      }
    }
  }
  else
    victim->pcdata->learned[sn] = value;

  return;
}

void learn_from_success ( CHAR_DATA * ch, int sn )
{
  int adept, gain, sklvl, percent, schance, lchance, learn;
  /*
   * the following added by tommi Jan 6006 inspired by Tarl of AFKmud
   * the point of the following code is to make skills learn at a much
   * slower rate than normal
   */

  if ( skill_table[sn]->type == SKILL_WEAPON )
  {
    lchance = number_range ( 1, 10 );

    if ( lchance != 5 )
    {
      return;
    }
  }

  if ( skill_table[sn]->type == SKILL_SKILL )
  {
    lchance = number_range ( 1, 5 );

    if ( lchance != 3 )
    {
      return;
    }
  }

  if ( skill_table[sn]->type == SKILL_SPELL )
  {
    lchance = number_range ( 1, 3 );

    if ( lchance != 2 )
    {
      return;
    }
  }

  if ( IS_NPC ( ch ) || ch->pcdata->learned[sn] <= 0 )
    return;

  adept = GET_ADEPT ( ch, sn );

  sklvl = skill_table[sn]->skill_level[ch->Class];

  if ( sklvl == 0 )
    sklvl = ch->level;

  if ( ch->pcdata->learned[sn] < adept )
  {
    schance = ch->pcdata->learned[sn] + ( 5 * skill_table[sn]->difficulty );
    percent = number_percent(  );

    if ( percent >= schance )
      learn = 2;
    else
      if ( schance - percent > 25 )
        return;
      else
        learn = 1;

    ch->pcdata->learned[sn] = UMIN ( adept, ch->pcdata->learned[sn] + learn );

    if ( ch->pcdata->learned[sn] == adept ) /* fully learned! */
    {
      gain = 1500 * sklvl;
      set_char_color ( AT_WHITE, ch );
      ch_printf ( ch, "You are now an adept of %s!  You gain %d bonus experience!\n\r", skill_table[sn]->name, gain );
    }
    else
    {
      gain = 50 * sklvl;

      if ( !ch->fighting && sn != gsn_hide && sn != gsn_sneak )
      {
        set_char_color ( AT_WHITE, ch );
        ch_printf ( ch, "You gain %d experience points from your success!\n\r", gain );
      }
    }

    gain_exp ( ch, gain );
  }
}

void learn_from_failure ( CHAR_DATA * ch, int sn )
{
  int adept, schance, lchance;
  /*
   * the following added by tommi Jan 6006 inspited by Tarl of AFKmud
   * the point of the following code is to make skills learn at a much
   * slower rate than normal
   */

  if ( skill_table[sn]->type == SKILL_WEAPON )
  {
    lchance = number_range ( 1, 10 );

    if ( lchance != 5 )
    {
      return;
    }
  }

  if ( skill_table[sn]->type == SKILL_SKILL )
  {
    lchance = number_range ( 1, 5 );

    if ( lchance != 3 )
    {
      return;
    }
  }

  if ( skill_table[sn]->type == SKILL_SPELL )
  {
    lchance = number_range ( 1, 3 );

    if ( lchance != 2 )
    {
      return;
    }
  }

  if ( IS_NPC ( ch ) || ch->pcdata->learned[sn] <= 0 )
    return;

  schance = ch->pcdata->learned[sn] + ( 5 * skill_table[sn]->difficulty );

  if ( schance - number_percent(  ) > 25 )
    return;

  adept = GET_ADEPT ( ch, sn );

  if ( ch->pcdata->learned[sn] < ( adept - 1 ) )
  {
    ch->pcdata->learned[sn] += 1;
    send_to_char ( "You learn from your mistakes\n\r", ch );
  }
}

void do_gouge ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;
  AFFECT_DATA af;
  short dam;
  int schance;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !can_use_skill ( ch, 0, gsn_gouge ) )
  {
    send_to_char ( "You do not yet know of this skill.\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You can't get close enough while mounted.\n\r", ch );
    return;
  }

  if ( ( victim = who_fighting ( ch ) ) == NULL )
  {
    send_to_char ( "You aren't fighting anyone.\n\r", ch );
    return;
  }

  schance = ( ( get_curr_dex ( victim ) - get_curr_dex ( ch ) ) * 10 ) + 10;

  if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) )
    schance += sysdata.gouge_plr_vs_plr;

  if ( victim->fighting && victim->fighting->who != ch )
    schance += sysdata.gouge_nontank;

  if ( can_use_skill ( ch, ( number_percent(  ) + schance ), gsn_gouge ) )
  {
    dam = number_range ( 5, ch->level );
    global_retcode = damage ( ch, victim, dam, gsn_gouge );

    if ( global_retcode == rNONE )
    {
      if ( !IS_AFFECTED ( victim, AFF_BLIND ) )
      {
        af.type = gsn_blindness;
        af.location = APPLY_HITROLL;
        af.modifier = -6;

        if ( !IS_NPC ( victim ) && !IS_NPC ( ch ) )
          af.duration = ( ch->level + 10 ) / get_curr_con ( victim );
        else
          af.duration = 3 + ( ch->level / 15 );

        af.bitvector = meb ( AFF_BLIND );

        affect_to_char ( victim, &af );

        act ( AT_SKILL, "You can't see a thing!", victim, NULL, NULL, TO_CHAR );
      }

      WAIT_STATE ( ch, PULSE_VIOLENCE );

      if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) )
      {
        if ( number_bits ( 1 ) == 0 )
        {
          ch_printf ( ch, "%s looks momentarily dazed.\n\r", victim->name );
          send_to_char ( "You are momentarily dazed ...\n\r", victim );
          WAIT_STATE ( victim, PULSE_VIOLENCE );
        }
      }
      else
        WAIT_STATE ( victim, PULSE_VIOLENCE );

      /*
       * Taken out by request - put back in by Thoric
       * * This is how it was designed.  You'd be a tad stunned
       * * if someone gouged you in the eye.
       * * Mildly modified by Blodkai, Feb 1998 at request of
       * * of pkill Conclave (peaceful use remains the same)
       */
    }
    else
      if ( global_retcode == rVICT_DIED )
      {
        act ( AT_BLOOD, "Your fingers plunge into your victim's brain, causing immediate death!", ch, NULL, NULL, TO_CHAR );
      }

    if ( global_retcode != rCHAR_DIED && global_retcode != rBOTH_DIED )
      learn_from_success ( ch, gsn_gouge );
  }
  else
  {
    WAIT_STATE ( ch, skill_table[gsn_gouge]->beats );
    global_retcode = damage ( ch, victim, 0, gsn_gouge );
    learn_from_failure ( ch, gsn_gouge );
  }

  return;
}

void do_detrap ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  OBJ_DATA *trap;
  int percent;
  bool found = FALSE;

  switch ( ch->substate )
  {

    default:

      if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
      {
        send_to_char ( "You can't concentrate enough for that.\n\r", ch );
        return;
      }

      argument = one_argument ( argument, arg );

      if ( !can_use_skill ( ch, 0, gsn_detrap ) )
      {
        send_to_char ( "You do not yet know of this skill.\n\r", ch );
        return;
      }

      if ( arg[0] == STRING_NULL )
      {
        send_to_char ( "Detrap what?\n\r", ch );
        return;
      }

      if ( ms_find_obj ( ch ) )
        return;

      found = FALSE;

      if ( ch->mount )
      {
        send_to_char ( "You can't do that while mounted.\n\r", ch );
        return;
      }

      if ( !ch->in_room->first_content )
      {
        send_to_char ( "You can't find that here.\n\r", ch );
        return;
      }

      for ( obj = ch->in_room->first_content; obj; obj = obj->next_content )
      {
        if ( can_see_obj ( ch, obj ) && nifty_is_name ( arg, obj->name ) )
        {
          found = TRUE;
          break;
        }
      }

      if ( !found )
      {
        send_to_char ( "You can't find that here.\n\r", ch );
        return;
      }

      act ( AT_ACTION, "You carefully begin your attempt to remove a trap from $p...", ch, obj, NULL, TO_CHAR );

      act ( AT_ACTION, "$n carefully attempts to remove a trap from $p...", ch, obj, NULL, TO_ROOM );
      ch->alloc_ptr = str_dup ( obj->name );
      add_timer ( ch, TIMER_DO_FUN, 3, do_detrap, 1 );
      /*
       * WAIT_STATE( ch, skill_table[gsn_detrap]->beats );
       */
      return;

    case 1:

      if ( !ch->alloc_ptr )
      {
        send_to_char ( "Your detrapping was interrupted!\n\r", ch );
        bug ( "do_detrap: ch->alloc_ptr NULL!", 0 );
        return;
      }

      strcpy ( arg, ch->alloc_ptr );

      DISPOSE ( ch->alloc_ptr );
      ch->alloc_ptr = NULL;
      ch->substate = SUB_NONE;
      break;

    case SUB_TIMER_DO_ABORT:
      DISPOSE ( ch->alloc_ptr );
      ch->substate = SUB_NONE;
      send_to_char ( "You carefully stop what you were doing.\n\r", ch );
      return;
  }

  if ( !ch->in_room->first_content )
  {
    send_to_char ( "You can't find that here.\n\r", ch );
    return;
  }

  for ( obj = ch->in_room->first_content; obj; obj = obj->next_content )
  {
    if ( can_see_obj ( ch, obj ) && nifty_is_name ( arg, obj->name ) )
    {
      found = TRUE;
      break;
    }
  }

  if ( !found )
  {
    send_to_char ( "You can't find that here.\n\r", ch );
    return;
  }

  if ( ( trap = get_trap ( obj ) ) == NULL )
  {
    send_to_char ( "You find no trap on that.\n\r", ch );
    return;
  }

  percent = number_percent(  ) - ( ch->level / 15 ) - ( get_curr_lck ( ch ) - 16 );

  separate_obj ( obj );

  if ( !can_use_skill ( ch, percent, gsn_detrap ) )
  {
    send_to_char ( "Ooops!\n\r", ch );
    spring_trap ( ch, trap );
    learn_from_failure ( ch, gsn_detrap );
    return;
  }

  extract_obj ( trap );

  send_to_char ( "You successfully remove a trap.\n\r", ch );
  learn_from_success ( ch, gsn_detrap );
  return;
}

void do_dig ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  OBJ_DATA *startobj;
  bool found, shovel;
  EXIT_DATA *pexit;

  switch ( ch->substate )
  {

    default:

      if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
      {
        send_to_char ( "You can't concentrate enough for that.\n\r", ch );
        return;
      }

      if ( ch->mount )
      {
        send_to_char ( "You can't do that while mounted.\n\r", ch );
        return;
      }

      one_argument ( argument, arg );

      if ( arg[0] != STRING_NULL )
      {
        if ( ( pexit = find_door ( ch, arg, TRUE ) ) == NULL && get_dir ( arg ) == -1 )
        {
          send_to_char ( "What direction is that?\n\r", ch );
          return;
        }

        if ( pexit )
        {
          if ( !IS_SET ( pexit->exit_info, EX_DIG ) && !IS_SET ( pexit->exit_info, EX_CLOSED ) )
          {
            send_to_char ( "There is no need to dig out that exit.\n\r", ch );
            return;
          }
        }
      }
      else
      {
        int sector;

        if ( IS_PLR_FLAG ( ch, PLR_ONMAP ) || IS_ACT_FLAG ( ch, ACT_ONMAP ) )
          sector = map_sector[ch->map][ch->x][ch->y];
        else
          sector = ch->in_room->sector_type;

        switch ( sector )
        {

          case SECT_CITY:
            send_to_char ( "The road is too hard to dig through.\n\r", ch );
            return;

          case SECT_INSIDE:
            send_to_char ( "The floor is too hard to dig through.\n\r", ch );
            return;

          case SECT_WATER_SWIM:

          case SECT_WATER_NOSWIM:

          case SECT_UNDERWATER:
            send_to_char ( "You cannot dig here.\n\r", ch );
            return;

          case SECT_AIR:
            send_to_char ( "What?  In the air?!\n\r", ch );
            return;
        }
      }

      add_timer ( ch, TIMER_DO_FUN, UMIN ( skill_table[gsn_dig]->beats / 10, 3 ), do_dig, 1 );

      ch->alloc_ptr = str_dup ( arg );
      send_to_char ( "You begin digging...\n\r", ch );
      act ( AT_PLAIN, "$n begins digging...", ch, NULL, NULL, TO_ROOM );
      return;

    case 1:

      if ( !ch->alloc_ptr )
      {
        send_to_char ( "Your digging was interrupted!\n\r", ch );
        act ( AT_PLAIN, "$n's digging was interrupted!", ch, NULL, NULL, TO_ROOM );
        bug ( "do_dig: alloc_ptr NULL", 0 );
        return;
      }

      strcpy ( arg, ch->alloc_ptr );

      DISPOSE ( ch->alloc_ptr );
      break;

    case SUB_TIMER_DO_ABORT:
      DISPOSE ( ch->alloc_ptr );
      ch->substate = SUB_NONE;
      send_to_char ( "You stop digging...\n\r", ch );
      act ( AT_PLAIN, "$n stops digging...", ch, NULL, NULL, TO_ROOM );
      return;
  }

  ch->substate = SUB_NONE;

  /*
   * not having a shovel makes it harder to succeed
   */
  shovel = FALSE;

  for ( obj = ch->first_carrying; obj; obj = obj->next_content )
    if ( obj->item_type == ITEM_SHOVEL )
    {
      shovel = TRUE;
      break;
    }

  /*
   * dig out an EX_DIG exit...
   */
  if ( arg[0] != STRING_NULL )
  {
    if ( ( pexit = find_door ( ch, arg, TRUE ) ) != NULL && IS_SET ( pexit->exit_info, EX_DIG ) && IS_SET ( pexit->exit_info, EX_CLOSED ) )
    {
      /*
       * 4 times harder to dig open a passage without a shovel
       */
      /*
       * if ( (number_percent() * (shovel ? 1 : 4)) <
       * LEARNED(ch, gsn_dig) )
       */
      if ( can_use_skill ( ch, ( number_percent(  ) * ( shovel ? 1 : 4 ) ), gsn_dig ) )
      {
        REMOVE_BIT ( pexit->exit_info, EX_CLOSED );
        send_to_char ( "You dig open a passageway!\n\r", ch );
        act ( AT_PLAIN, "$n digs open a passageway!", ch, NULL, NULL, TO_ROOM );
        learn_from_success ( ch, gsn_dig );
        return;
      }
    }

    learn_from_failure ( ch, gsn_dig );

    send_to_char ( "Your dig did not discover any exit...\n\r", ch );
    act ( AT_PLAIN, "$n's dig did not discover any exit...", ch, NULL, NULL, TO_ROOM );
    return;
  }

  startobj = ch->in_room->first_content;

  found = FALSE;

  for ( obj = startobj; obj; obj = obj->next_content )
  {
    /*
     * twice as hard to find something without a shovel
     */
    if ( IS_OBJ_STAT ( obj, ITEM_BURIED ) && ( can_use_skill ( ch, ( number_percent(  ) * ( shovel ? 1 : 2 ) ), gsn_dig ) ) )
      /*
       * &&  (number_percent() * (shovel ? 1 : 2)) <
       * (IS_NPC(ch) ? 80 : ch->pcdata->learned[gsn_dig]) )
       */
    {
      found = TRUE;
      break;
    }
  }

  if ( !found )
  {
    send_to_char ( "Your dig uncovered nothing.\n\r", ch );
    act ( AT_PLAIN, "$n's dig uncovered nothing.", ch, NULL, NULL, TO_ROOM );
    learn_from_failure ( ch, gsn_dig );
    return;
  }

  separate_obj ( obj );

  xREMOVE_BIT ( obj->extra_flags, ITEM_BURIED );
  act ( AT_SKILL, "Your dig uncovered $p!", ch, obj, NULL, TO_CHAR );
  act ( AT_SKILL, "$n's dig uncovered $p!", ch, obj, NULL, TO_ROOM );
  learn_from_success ( ch, gsn_dig );

  if ( obj->item_type == ITEM_CORPSE_PC || obj->item_type == ITEM_CORPSE_NPC )
    adjust_favor ( ch, 14, 1 );

  return;
}

void do_search ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  OBJ_DATA *container;
  OBJ_DATA *startobj;
  int percent, door;
  door = -1;

  switch ( ch->substate )
  {

    default:

      if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
      {
        send_to_char ( "You can't concentrate enough for that.\n\r", ch );
        return;
      }

      if ( ch->mount )
      {
        send_to_char ( "You can't do that while mounted.\n\r", ch );
        return;
      }

      argument = one_argument ( argument, arg );

      if ( arg[0] != STRING_NULL && ( door = get_door ( arg ) ) == -1 )
      {
        container = get_obj_here ( ch, arg );

        if ( !container )
        {
          send_to_char ( "You can't find that here.\n\r", ch );
          return;
        }

        if ( container->item_type != ITEM_CONTAINER )
        {
          send_to_char ( "You can't search in that!\n\r", ch );
          return;
        }

        if ( IS_SET ( container->value[1], CONT_CLOSED ) )
        {
          send_to_char ( "It is closed.\n\r", ch );
          return;
        }
      }

      add_timer ( ch, TIMER_DO_FUN, UMIN ( skill_table[gsn_search]->beats / 10, 3 ), do_search, 1 );

      send_to_char ( "You begin your search...\n\r", ch );
      ch->alloc_ptr = str_dup ( arg );
      return;

    case 1:

      if ( !ch->alloc_ptr )
      {
        send_to_char ( "Your search was interrupted!\n\r", ch );
        bug ( "do_search: alloc_ptr NULL", 0 );
        return;
      }

      strcpy ( arg, ch->alloc_ptr );

      DISPOSE ( ch->alloc_ptr );
      break;

    case SUB_TIMER_DO_ABORT:
      DISPOSE ( ch->alloc_ptr );
      ch->substate = SUB_NONE;
      send_to_char ( "You stop your search...\n\r", ch );
      return;
  }

  ch->substate = SUB_NONE;

  if ( arg[0] == STRING_NULL )
    startobj = ch->in_room->first_content;
  else
  {
    if ( ( door = get_door ( arg ) ) != -1 )
      startobj = NULL;
    else
    {
      container = get_obj_here ( ch, arg );

      if ( !container )
      {
        send_to_char ( "You can't find that here.\n\r", ch );
        return;
      }

      startobj = container->first_content;
    }
  }

  if ( ( !startobj && door == -1 ) || IS_NPC ( ch ) )
  {
    send_to_char ( "You find nothing.\n\r", ch );
    learn_from_failure ( ch, gsn_search );
    return;
  }

  percent = number_percent(  ) + number_percent(  ) - ( ch->level / 10 );

  if ( door != -1 )
  {
    EXIT_DATA *pexit;

    if ( ( pexit = get_exit ( ch->in_room, door ) ) != NULL
         && IS_SET ( pexit->exit_info, EX_SECRET ) && IS_SET ( pexit->exit_info, EX_xSEARCHABLE ) && can_use_skill ( ch, percent, gsn_search ) )
    {
      act ( AT_SKILL, "Your search reveals the $d!", ch, NULL, pexit->keyword, TO_CHAR );
      act ( AT_SKILL, "$n finds the $d!", ch, NULL, pexit->keyword, TO_ROOM );
      REMOVE_BIT ( pexit->exit_info, EX_SECRET );
      learn_from_success ( ch, gsn_search );
      return;
    }
  }
  else
    for ( obj = startobj; obj; obj = obj->next_content )
    {
      if ( IS_OBJ_STAT ( obj, ITEM_HIDDEN ) && can_use_skill ( ch, percent, gsn_search ) )
      {
        separate_obj ( obj );
        xREMOVE_BIT ( obj->extra_flags, ITEM_HIDDEN );
        act ( AT_SKILL, "Your search reveals $p!", ch, obj, NULL, TO_CHAR );
        act ( AT_SKILL, "$n finds $p!", ch, obj, NULL, TO_ROOM );
        learn_from_success ( ch, gsn_search );
        return;
      }
    }

  send_to_char ( "You find nothing.\n\r", ch );

  learn_from_failure ( ch, gsn_search );
  return;
}

void do_steal ( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  CHAR_DATA *victim, *mst;
  OBJ_DATA *obj;
  int percent;
  argument = one_argument ( argument, arg1 );
  argument = one_argument ( argument, arg2 );

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  if ( arg1[0] == STRING_NULL || arg2[0] == STRING_NULL )
  {
    send_to_char ( "Steal what from whom?\n\r", ch );
    return;
  }

  if ( ms_find_obj ( ch ) )
    return;

  if ( ( victim = get_char_room ( ch, arg2 ) ) == NULL )
  {
    send_to_char ( "They aren't here.\n\r", ch );
    return;
  }

  if ( victim == ch )
  {
    send_to_char ( "That's pointless.\n\r", ch );
    return;
  }

  if ( xIS_SET ( ch->in_room->room_flags, ROOM_SAFE ) )
  {
    set_char_color ( AT_MAGIC, ch );
    send_to_char ( "A magical force interrupts you.\n\r", ch );
    return;
  }

  /*
   * Disabled stealing among players because of complaints naked avatars were
   * running around stealing eq from equipped pkillers. -- Narn
   */
  /*
   * if ( check_illegal_psteal( ch, victim ) )
   * {
   * send_to_char( "You can't steal from that player.\n\r", ch );
   * return;
   * }
   */
  if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) )
  {
    set_char_color ( AT_IMMORT, ch );
    send_to_char ( "The gods forbid theft between players.\n\r", ch );
    return;
  }

  if ( xIS_SET ( victim->act, ACT_PACIFIST ) ) /* Gorog */
  {
    send_to_char ( "They are a pacifist - Shame on you!\n\r", ch );
    return;
  }

  WAIT_STATE ( ch, skill_table[gsn_steal]->beats );

  percent = number_percent(  ) + ( IS_AWAKE ( victim ) ? 10 : -50 ) - ( get_curr_lck ( ch ) - 15 ) + ( get_curr_lck ( victim ) - 13 );
  /*
   * Changed the level check, made it 10 levels instead of five and made the
   * victim not attack in the case of a too high level difference.  This is
   * to allow mobprogs where the mob steals eq without having to put level
   * checks into the progs.  Also gave the mobs a 10% chance of failure.
   */

  if ( ch->level + 10 < victim->level )
  {
    send_to_char ( "You really don't want to try that!\n\r", ch );
    return;
  }

  if ( victim->position == POS_FIGHTING || !can_use_skill ( ch, percent, gsn_steal ) )
  {
    /*
     * Failure.
     */
    send_to_char ( "Oops...\n\r", ch );
    act ( AT_ACTION, "$n tried to steal from you!\n\r", ch, NULL, victim, TO_VICT );
    act ( AT_ACTION, "$n tried to steal from $N.\n\r", ch, NULL, victim, TO_NOTVICT );
    sprintf ( buf, "%s is a bloody thief!", ch->name );
    do_yell ( victim, buf );
    learn_from_failure ( ch, gsn_steal );

    if ( !IS_NPC ( ch ) )
    {
      if ( legal_loot ( ch, victim ) )
      {
        global_retcode = multi_hit ( victim, ch, TYPE_UNDEFINED );
      }
      else
      {
        /*
         * log_string( buf );
         */
        if ( IS_NPC ( ch ) )
        {
          if ( ( mst = ch->master ) == NULL )
            return;
        }
        else
          mst = ch;

        if ( IS_NPC ( mst ) )
          return;

        if ( !xIS_SET ( mst->act, PLR_THIEF ) )
        {
          xSET_BIT ( mst->act, PLR_THIEF );
          set_char_color ( AT_WHITE, ch );
          send_to_char ( "A strange feeling grows deep inside you, and a tingle goes up your spine...\n\r", ch );
          set_char_color ( AT_IMMORT, ch );
          send_to_char ( "A deep voice booms inside your head, 'Thou shall now be known as a lowly thief!'\n\r", ch );
          set_char_color ( AT_WHITE, ch );
          send_to_char ( "You feel as if your soul has been revealed for all to see.\n\r", ch );
          save_char_obj ( mst );
        }
      }
    }

    return;
  }

  if ( !str_cmp ( arg1, "coin" ) || !str_cmp ( arg1, "coins" ) || !str_cmp ( arg1, "gold" ) )
  {
    int amount;
    amount = ( int ) ( victim->gold * number_range ( 1, 10 ) / 100 );

    if ( amount <= 0 )
    {
      send_to_char ( "You couldn't get any gold.\n\r", ch );
      learn_from_failure ( ch, gsn_steal );
      return;
    }

    ch->gold += amount;

    victim->gold -= amount;
    ch_printf ( ch, "Aha!  You got %d gold coins.\n\r", amount );
    learn_from_success ( ch, gsn_steal );
    return;
  }

  if ( ( obj = get_obj_carry ( victim, arg1 ) ) == NULL )
  {
    send_to_char ( "You can't seem to find it.\n\r", ch );
    learn_from_failure ( ch, gsn_steal );
    return;
  }

  if ( !can_drop_obj ( ch, obj ) || IS_OBJ_STAT ( obj, ITEM_INVENTORY ) || IS_OBJ_STAT ( obj, ITEM_PROTOTYPE ) || obj->level > ch->level )
  {
    send_to_char ( "You can't manage to pry it away.\n\r", ch );
    learn_from_failure ( ch, gsn_steal );
    return;
  }

  if ( ch->carry_number + ( get_obj_number ( obj ) / obj->count ) > can_carry_n ( ch ) )
  {
    send_to_char ( "You have your hands full.\n\r", ch );
    learn_from_failure ( ch, gsn_steal );
    return;
  }

  if ( ch->carry_weight + ( get_obj_weight ( obj ) / obj->count ) > can_carry_w ( ch ) )
  {
    send_to_char ( "You can't carry that much weight.\n\r", ch );
    learn_from_failure ( ch, gsn_steal );
    return;
  }

  separate_obj ( obj );

  obj_from_char ( obj );
  obj_to_char ( obj, ch );
  send_to_char ( "Ok.\n\r", ch );
  learn_from_success ( ch, gsn_steal );
  adjust_favor ( ch, 9, 1 );
  return;
}

void do_backstab ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  OBJ_DATA *obj;
  int percent;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't do that right now.\n\r", ch );
    return;
  }

  one_argument ( argument, arg );

  if ( ch->mount )
  {
    send_to_char ( "You can't get close enough while mounted.\n\r", ch );
    return;
  }

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Backstab whom?\n\r", ch );
    return;
  }

  if ( ( victim = get_char_room ( ch, arg ) ) == NULL )
  {
    send_to_char ( "They aren't here.\n\r", ch );
    return;
  }

  if ( victim == ch )
  {
    send_to_char ( "How can you sneak up on yourself?\n\r", ch );
    return;
  }

  if ( is_safe ( ch, victim, TRUE ) )
    return;

  if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) && xIS_SET ( ch->act, PLR_NICE ) )
  {
    send_to_char ( "You are too nice to do that.\n\r", ch );
    return;
  }

  /*
   * Added stabbing weapon. -Narn
   */
  if ( ! ( obj = get_eq_char ( ch, WEAR_WIELD ) ) )
  {
    send_to_char ( "You need to wield a piercing weapon.\n\r", ch );
    return;
  }

  if ( obj->value[3] != DAM_PIERCE )
  {
    send_to_char ( "You need to wield a piercing weapon to backstab.\n\r", ch );
    return;
  }

  if ( victim->fighting )
  {
    send_to_char ( "You can't backstab someone who is in combat.\n\r", ch );
    return;
  }

  if ( IS_AFFECTED ( victim, AFF_SNEAK ) )
  {
    act ( AT_RED, "$N see's you comming and spins around deflecting your backstab.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "You see $n attempt to backstab you, but deftly move out of the way to counter attack.", ch, NULL, victim, TO_VICT );
    global_retcode = damage ( victim, ch, 100, gsn_backstab );
    check_illegal_pk ( ch, victim );
    return;
  }

  /*
   * Can backstab a char even if it's hurt as long as it's sleeping. -Narn
   */
  if ( victim->hit < victim->max_hit && IS_AWAKE ( victim ) )
  {
    act ( AT_PLAIN, "$N is hurt and suspicious ... you can't sneak up.", ch, NULL, victim, TO_CHAR );
    return;
  }

  percent = number_percent(  ) - ( get_curr_lck ( ch ) - 14 ) + ( get_curr_lck ( victim ) - 13 );

  check_attacker ( ch, victim );
  WAIT_STATE ( ch, skill_table[gsn_backstab]->beats );

  if ( !IS_AWAKE ( victim ) || can_use_skill ( ch, percent, gsn_backstab ) )
  {
    learn_from_success ( ch, gsn_backstab );
    act ( AT_RED, "You move swiftly behind $N and stab deeply into their back.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N crys out in pain from $n's backstab.", ch, NULL, victim, TO_ROOM );
    global_retcode = multi_hit ( ch, victim, gsn_backstab );
    adjust_favor ( ch, 10, 1 );
    check_illegal_pk ( ch, victim );
  }
  else
  {
    learn_from_failure ( ch, gsn_backstab );
    act ( AT_RED, "You fail to backstab $N.", ch, NULL, victim, TO_CHAR );
    global_retcode = damage ( ch, victim, 0, gsn_backstab );
    check_illegal_pk ( ch, victim );
  }

  return;
}

void do_rescue ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  CHAR_DATA *fch;
  int percent;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( IS_AFFECTED ( ch, AFF_BERSERK ) )
  {
    send_to_char ( "You aren't thinking clearly...\n\r", ch );
    return;
  }

  one_argument ( argument, arg );

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Rescue whom?\n\r", ch );
    return;
  }

  if ( ( victim = get_char_room ( ch, arg ) ) == NULL )
  {
    send_to_char ( "They aren't here.\n\r", ch );
    return;
  }

  if ( victim == ch )
  {
    send_to_char ( "How about fleeing instead?\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && IS_NPC ( victim ) )
  {
    send_to_char ( "They don't need your help!\n\r", ch );
    return;
  }

  if ( !ch->fighting )
  {
    send_to_char ( "Too late...\n\r", ch );
    return;
  }

  if ( ( fch = who_fighting ( victim ) ) == NULL )
  {
    send_to_char ( "They are not fighting right now.\n\r", ch );
    return;
  }

  if ( who_fighting ( victim ) == ch )
  {
    send_to_char ( "Just running away would be better...\n\r", ch );
    return;
  }

  if ( IS_AFFECTED ( victim, AFF_BERSERK ) )
  {
    send_to_char ( "Stepping in front of a berserker would not be an intelligent decision.\n\r", ch );
    return;
  }

  percent = number_percent(  ) - ( get_curr_lck ( ch ) - 14 ) - ( get_curr_lck ( victim ) - 16 );

  WAIT_STATE ( ch, skill_table[gsn_rescue]->beats );

  if ( !can_use_skill ( ch, percent, gsn_rescue ) )
  {
    send_to_char ( "You fail the rescue.\n\r", ch );
    act ( AT_SKILL, "$n tries to rescue you!", ch, NULL, victim, TO_VICT );
    act ( AT_SKILL, "$n tries to rescue $N!", ch, NULL, victim, TO_NOTVICT );
    learn_from_failure ( ch, gsn_rescue );
    return;
  }

  act ( AT_SKILL, "You rescue $N!", ch, NULL, victim, TO_CHAR );

  act ( AT_SKILL, "$n rescues you!", ch, NULL, victim, TO_VICT );
  act ( AT_SKILL, "$n moves in front of $N!", ch, NULL, victim, TO_NOTVICT );
  learn_from_success ( ch, gsn_rescue );
  adjust_favor ( ch, 8, 1 );
  stop_fighting ( fch, FALSE );
  stop_fighting ( victim, FALSE );

  if ( ch->fighting )
    stop_fighting ( ch, FALSE );

  set_fighting ( ch, fch );

  set_fighting ( fch, ch );

  return;
}

void do_kick ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_kick]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    if ( ( victim = who_fighting ( ch ) ) == NULL )
    {
      send_to_char ( "You aren't fighting anyone.\n\r", ch );
      return;
    }
  }
  else
    if ( argument[0] != STRING_NULL )
    {
      if ( ( victim = get_char_room ( ch, argument ) ) == NULL )
      {
        send_to_char ( "They aren't here.\n\r", ch );
        return;
      }
    }

  WAIT_STATE ( ch, skill_table[gsn_kick]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_kick ) )
  {
    learn_from_success ( ch, gsn_kick );
    act ( AT_RED, "You kick $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets kicked by $n", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, number_range ( 15, ch->level * 2 ), gsn_kick );
  }
  else
  {
    learn_from_failure ( ch, gsn_kick );
    act ( AT_RED, "Your kick misses $N.", ch, NULL, victim, TO_CHAR );
    global_retcode = damage ( ch, victim, 0, gsn_kick );
  }

  return;
}

void do_headbutt ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_headbutt]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    if ( ( victim = who_fighting ( ch ) ) == NULL )
    {
      send_to_char ( "You aren't fighting anyone.\n\r", ch );
      return;
    }
  }
  else
    if ( argument[0] != STRING_NULL )
    {
      if ( ( victim = get_char_room ( ch, argument ) ) == NULL )
      {
        send_to_char ( "They aren't here.\n\r", ch );
        return;
      }
    }

  WAIT_STATE ( ch, skill_table[gsn_headbutt]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_headbutt ) )
  {
    learn_from_success ( ch, gsn_headbutt );
    act ( AT_RED, "You headbutt $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets headbutted by $n", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, number_range ( 15, ch->level * 2 ), gsn_headbutt );
  }
  else
  {
    learn_from_failure ( ch, gsn_headbutt );
    act ( AT_RED, "Your headbutt misses $N.", ch, NULL, victim, TO_CHAR );
    global_retcode = damage ( ch, victim, 0, gsn_headbutt );
  }

  return;
}

void do_roundhouse ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_roundhouse]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    if ( ( victim = who_fighting ( ch ) ) == NULL )
    {
      send_to_char ( "You aren't fighting anyone.\n\r", ch );
      return;
    }
  }
  else
    if ( argument[0] != STRING_NULL )
    {
      if ( ( victim = get_char_room ( ch, argument ) ) == NULL )
      {
        send_to_char ( "They aren't here.\n\r", ch );
        return;
      }
    }

  WAIT_STATE ( ch, skill_table[gsn_roundhouse]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_roundhouse ) )
  {
    learn_from_success ( ch, gsn_roundhouse );
    act ( AT_RED, "You roundhouse $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets roundhouseed by $n", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, number_range ( 15, ch->level * 2 ), gsn_roundhouse );
  }
  else
  {
    learn_from_failure ( ch, gsn_roundhouse );
    act ( AT_RED, "Your roundhouse misses $N.", ch, NULL, victim, TO_CHAR );
    global_retcode = damage ( ch, victim, 0, gsn_roundhouse );
  }

  return;
}

void do_punch ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_punch]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    if ( ( victim = who_fighting ( ch ) ) == NULL )
    {
      send_to_char ( "You aren't fighting anyone.\n\r", ch );
      return;
    }
  }
  else
    if ( argument[0] != STRING_NULL )
    {
      if ( ( victim = get_char_room ( ch, argument ) ) == NULL )
      {
        send_to_char ( "They aren't here.\n\r", ch );
        return;
      }
    }

  WAIT_STATE ( ch, skill_table[gsn_punch]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_punch ) )
  {
    learn_from_success ( ch, gsn_punch );
    act ( AT_RED, "You punch $N in the face.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets a well timed punch in the face from $n.", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, number_range ( 15, ch->level * 2 ), gsn_punch );
  }
  else
  {
    learn_from_failure ( ch, gsn_punch );
    act ( AT_RED, "Your punch flys wildly through the air missing $N.", ch, NULL, victim, TO_CHAR );
    global_retcode = damage ( ch, victim, 0, gsn_punch );
  }

  return;
}

void do_bash ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;
  int schance;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_bash]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( ( victim = who_fighting ( ch ) ) == NULL )
  {
    send_to_char ( "You aren't fighting anyone.\n\r", ch );
    return;
  }

  schance = ( ( ( get_curr_dex ( victim ) + get_curr_str ( victim ) ) - ( get_curr_dex ( ch ) + get_curr_str ( ch ) ) ) * 10 ) + 10;

  if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) )
    schance += sysdata.bash_plr_vs_plr;

  if ( victim->fighting && victim->fighting->who != ch )
    schance += sysdata.bash_nontank;

  WAIT_STATE ( ch, skill_table[gsn_bash]->beats );

  if ( can_use_skill ( ch, ( number_percent(  ) + schance ), gsn_bash ) )
  {
    learn_from_success ( ch, gsn_bash );
    /*
     * do not change anything here!  -Thoric
     */
    WAIT_STATE ( victim, 2 * PULSE_VIOLENCE );
    victim->position = POS_SITTING;
    act ( AT_RED, "You run head first at $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets slammed by a well timed bash from $n.", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, number_range ( 15, ch->level ) * 2, gsn_bash );
  }
  else
  {
    learn_from_failure ( ch, gsn_bash );
    act ( AT_RED, "You fall flat on your face.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$n trips and falls flat on his face.", ch, NULL, victim, TO_ROOM );
    global_retcode = damage ( ch, victim, 0, gsn_bash );
  }

  return;
}

void do_stun ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;
  AFFECT_DATA af;
  int schance;
  bool fail;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_stun]->skill_level[ch->Class] )
  {
    send_to_char ( "You better leave the martial arts to fighters.\n\r", ch );
    return;
  }

  if ( ( victim = who_fighting ( ch ) ) == NULL )
  {
    send_to_char ( "You aren't fighting anyone.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->move < ch->max_move / 10 )
  {
    set_char_color ( AT_SKILL, ch );
    send_to_char ( "You are far too tired to do that.\n\r", ch );
    return;  /* missing return fixed March 11/96 */
  }

  WAIT_STATE ( ch, skill_table[gsn_stun]->beats );

  fail = FALSE;
  schance = ris_save ( victim, ch->level, RIS_PARALYSIS );

  if ( schance == 1000 )
    fail = TRUE;
  else
    fail = saves_para_petri ( schance, victim );

  schance = ( ( ( get_curr_dex ( victim ) + get_curr_str ( victim ) ) - ( get_curr_dex ( ch ) + get_curr_str ( ch ) ) ) * 10 ) + 10;

  /*
   * harder for player to stun another player
   */
  if ( !IS_NPC ( ch ) && !IS_NPC ( victim ) )
    schance += sysdata.stun_plr_vs_plr;
  else
    schance += sysdata.stun_regular;

  if ( !fail && can_use_skill ( ch, ( number_percent(  ) + schance ), gsn_stun ) )
  {
    learn_from_success ( ch, gsn_stun );
    /*
     * DO *NOT* CHANGE!    -Thoric
     */

    if ( !IS_NPC ( ch ) )
      ch->move -= ch->max_move / 10;

    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );

    WAIT_STATE ( victim, PULSE_VIOLENCE );

    act ( AT_SKILL, "$N smashes into you, leaving you stunned!", victim, NULL, ch, TO_CHAR );

    act ( AT_SKILL, "You smash into $N, leaving $M stunned!", ch, NULL, victim, TO_CHAR );

    act ( AT_SKILL, "$n smashes into $N, leaving $M stunned!", ch, NULL, victim, TO_NOTVICT );

    if ( !IS_AFFECTED ( victim, AFF_PARALYSIS ) )
    {
      af.type = gsn_stun;
      af.location = APPLY_AC;
      af.modifier = 20;
      af.duration = 3;
      af.bitvector = meb ( AFF_PARALYSIS );
      affect_to_char ( victim, &af );
      update_pos ( victim );
    }
  }
  else
  {
    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );

    if ( !IS_NPC ( ch ) )
      ch->move -= ch->max_move / 15;

    learn_from_failure ( ch, gsn_stun );

    act ( AT_SKILL, "$n charges at you screaming, but you dodge out of the way.", ch, NULL, victim, TO_VICT );

    act ( AT_SKILL, "You try to stun $N, but $E dodges out of the way.", ch, NULL, victim, TO_CHAR );

    act ( AT_SKILL, "$n charges screaming at $N, but keeps going right on past.", ch, NULL, victim, TO_NOTVICT );
  }

  return;
}

/*
 * Disarm a creature.
 * Caller must check for successful attack.
 * Check for loyalty flag (weapon disarms to inventory) for pkillers -Blodkai
 */
void disarm ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  OBJ_DATA *obj, *tmpobj;

  if ( ( obj = get_eq_char ( victim, WEAR_WIELD ) ) == NULL )
    return;

  if ( ( tmpobj = get_eq_char ( victim, WEAR_DUAL_WIELD ) ) != NULL && number_bits ( 1 ) == 0 )
    obj = tmpobj;

  if ( get_eq_char ( ch, WEAR_WIELD ) == NULL && number_bits ( 1 ) == 0 )
  {
    learn_from_failure ( ch, gsn_disarm );
    return;
  }

  if ( IS_NPC ( ch ) && !can_see_obj ( ch, obj ) && number_bits ( 1 ) == 0 )
  {
    learn_from_failure ( ch, gsn_disarm );
    return;
  }

  if ( check_grip ( ch, victim ) )
  {
    learn_from_failure ( ch, gsn_disarm );
    return;
  }

  act ( AT_RED, "$n DISARMS you, sending your weapon flying accross the room!", ch, NULL, victim, TO_VICT );

  act ( AT_SKILL, "You disarm $N sending their weapon flying accross the room", ch, NULL, victim, TO_CHAR );
  act ( AT_SKILL, "$n disarms $N!", ch, NULL, victim, TO_NOTVICT );
  learn_from_success ( ch, gsn_disarm );

  if ( obj == get_eq_char ( victim, WEAR_WIELD ) && ( tmpobj = get_eq_char ( victim, WEAR_DUAL_WIELD ) ) != NULL )
    tmpobj->wear_loc = WEAR_WIELD;

  obj_from_char ( obj );

  obj_to_char ( obj, victim );

  return;
}

void do_disarm ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;
  OBJ_DATA *obj;
  int percent;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_disarm]->skill_level[ch->Class] )
  {
    send_to_char ( "You don't know how to disarm opponents.\n\r", ch );
    return;
  }

  if ( get_eq_char ( ch, WEAR_WIELD ) == NULL )
  {
    send_to_char ( "You must wield a weapon to disarm.\n\r", ch );
    return;
  }

  if ( ( victim = who_fighting ( ch ) ) == NULL )
  {
    send_to_char ( "You aren't fighting anyone.\n\r", ch );
    return;
  }

  if ( ( obj = get_eq_char ( victim, WEAR_WIELD ) ) == NULL )
  {
    send_to_char ( "Your opponent is not wielding a weapon.\n\r", ch );
    return;
  }

  WAIT_STATE ( ch, skill_table[gsn_disarm]->beats );

  percent = number_percent(  ) + victim->level - ch->level - ( get_curr_lck ( ch ) - 15 ) + ( get_curr_lck ( victim ) - 15 );

  if ( !can_see_obj ( ch, obj ) )
    percent += 10;

  if ( can_use_skill ( ch, ( percent * 3 / 2 ), gsn_disarm ) )
    disarm ( ch, victim );
  else
  {
    send_to_char ( "You failed.\n\r", ch );
    learn_from_failure ( ch, gsn_disarm );
  }

  return;
}

/*
 * Trip a creature.
 * Caller must check for successful attack.
 */
void trip ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  if ( IS_AFFECTED ( victim, AFF_FLYING ) || IS_AFFECTED ( victim, AFF_FLOATING ) )
    return;

  if ( victim->mount )
  {
    if ( IS_AFFECTED ( victim->mount, AFF_FLYING ) || IS_AFFECTED ( victim->mount, AFF_FLOATING ) )
      return;

    act ( AT_SKILL, "$n trips your mount and you fall off!", ch, NULL, victim, TO_VICT );

    act ( AT_SKILL, "You trip $N's mount and $N falls off!", ch, NULL, victim, TO_CHAR );

    act ( AT_SKILL, "$n trips $N's mount and $N falls off!", ch, NULL, victim, TO_NOTVICT );

    xREMOVE_BIT ( victim->mount->act, ACT_MOUNTED );

    victim->mount = NULL;

    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );

    WAIT_STATE ( victim, 2 * PULSE_VIOLENCE );

    victim->position = POS_RESTING;

    return;
  }

  if ( victim->wait == 0 )
  {
    act ( AT_SKILL, "$n trips you and you go down!", ch, NULL, victim, TO_VICT );
    act ( AT_SKILL, "You trip $N and $N goes down!", ch, NULL, victim, TO_CHAR );
    act ( AT_SKILL, "$n trips $N and $N goes down!", ch, NULL, victim, TO_NOTVICT );
    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );
    WAIT_STATE ( victim, 2 * PULSE_VIOLENCE );
    victim->position = POS_RESTING;
  }

  return;
}

void do_pick ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *gch;
  OBJ_DATA *obj;
  EXIT_DATA *pexit;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  one_argument ( argument, arg );

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Pick what?\n\r", ch );
    return;
  }

  if ( ms_find_obj ( ch ) )
    return;

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  WAIT_STATE ( ch, skill_table[gsn_pick_lock]->beats );

  /*
   * look for guards
   */

  for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
  {
    if ( IS_NPC ( gch ) && IS_AWAKE ( gch ) && ch->level + 5 < gch->level )
    {
      act ( AT_PLAIN, "$N is standing too close to the lock.", ch, NULL, gch, TO_CHAR );
      return;
    }
  }

  if ( !can_use_skill ( ch, number_percent(  ), gsn_pick_lock ) )
  {
    send_to_char ( "You failed.\n\r", ch );
    learn_from_failure ( ch, gsn_pick_lock );
    /*
     * for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
     * {
     * if ( IS_NPC(gch) && IS_AWAKE(gch) && xIS_SET(gch->act, ACT_GUARDIAN ) )
     * multi_hit( gch, ch, TYPE_UNDEFINED );
     * }
     */
    return;
  }

  if ( ( pexit = find_door ( ch, arg, TRUE ) ) != NULL )
  {
    /*
     * 'pick door'
     */
    /*
     * ROOM_INDEX_DATA *to_room;
     *//*
 * Unused
 */
    EXIT_DATA *pexit_rev;

    if ( !IS_SET ( pexit->exit_info, EX_CLOSED ) )
    {
      send_to_char ( "It's not closed.\n\r", ch );
      return;
    }

    if ( pexit->key < 0 )
    {
      send_to_char ( "It can't be picked.\n\r", ch );
      return;
    }

    if ( !IS_SET ( pexit->exit_info, EX_LOCKED ) )
    {
      send_to_char ( "It's already unlocked.\n\r", ch );
      return;
    }

    if ( IS_SET ( pexit->exit_info, EX_PICKPROOF ) )
    {
      send_to_char ( "You failed.\n\r", ch );
      learn_from_failure ( ch, gsn_pick_lock );
      check_room_for_traps ( ch, TRAP_PICK | trap_door[pexit->vdir] );
      return;
    }

    REMOVE_BIT ( pexit->exit_info, EX_LOCKED );

    send_to_char ( "*Click*\n\r", ch );
    act ( AT_ACTION, "$n picks the $d.", ch, NULL, pexit->keyword, TO_ROOM );
    learn_from_success ( ch, gsn_pick_lock );
    adjust_favor ( ch, 9, 1 );
    /*
     * pick the other side
     */

    if ( ( pexit_rev = pexit->rexit ) != NULL && pexit_rev->to_room == ch->in_room )
    {
      REMOVE_BIT ( pexit_rev->exit_info, EX_LOCKED );
    }

    check_room_for_traps ( ch, TRAP_PICK | trap_door[pexit->vdir] );

    return;
  }

  if ( ( obj = get_obj_here ( ch, arg ) ) != NULL )
  {
    /*
     * 'pick object'
     */
    if ( obj->item_type != ITEM_CONTAINER )
    {
      send_to_char ( "That's not a container.\n\r", ch );
      return;
    }

    if ( !IS_SET ( obj->value[1], CONT_CLOSED ) )
    {
      send_to_char ( "It's not closed.\n\r", ch );
      return;
    }

    if ( obj->value[2] < 0 )
    {
      send_to_char ( "It can't be unlocked.\n\r", ch );
      return;
    }

    if ( !IS_SET ( obj->value[1], CONT_LOCKED ) )
    {
      send_to_char ( "It's already unlocked.\n\r", ch );
      return;
    }

    if ( IS_SET ( obj->value[1], CONT_PICKPROOF ) )
    {
      send_to_char ( "You failed.\n\r", ch );
      learn_from_failure ( ch, gsn_pick_lock );
      check_for_trap ( ch, obj, TRAP_PICK );
      return;
    }

    separate_obj ( obj );

    REMOVE_BIT ( obj->value[1], CONT_LOCKED );
    send_to_char ( "*Click*\n\r", ch );
    act ( AT_ACTION, "$n picks $p.", ch, obj, NULL, TO_ROOM );
    learn_from_success ( ch, gsn_pick_lock );
    adjust_favor ( ch, 9, 1 );
    check_for_trap ( ch, obj, TRAP_PICK );
    return;
  }

  ch_printf ( ch, "You see no %s here.\n\r", arg );

  return;
}

void do_sneak ( CHAR_DATA * ch, char *argument )
{
  AFFECT_DATA af;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  send_to_char ( "You attempt to move silently.\n\r", ch );

  affect_strip ( ch, gsn_sneak );

  if ( can_use_skill ( ch, number_percent(  ), gsn_sneak ) )
  {
    af.type = gsn_sneak;
    af.duration = ch->level * DUR_CONV;
    af.location = APPLY_NONE;
    af.modifier = 0;
    af.bitvector = meb ( AFF_SNEAK );
    affect_to_char ( ch, &af );
    learn_from_success ( ch, gsn_sneak );
  }
  else
    learn_from_failure ( ch, gsn_sneak );

  return;
}

void do_hide ( CHAR_DATA * ch, char *argument )
{
  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  send_to_char ( "You attempt to hide.\n\r", ch );

  if ( IS_AFFECTED ( ch, AFF_HIDE ) )
    xREMOVE_BIT ( ch->affected_by, AFF_HIDE );

  if ( can_use_skill ( ch, number_percent(  ), gsn_hide ) )
  {
    xSET_BIT ( ch->affected_by, AFF_HIDE );
    learn_from_success ( ch, gsn_hide );
  }
  else
    learn_from_failure ( ch, gsn_hide );

  return;
}

/*
 * Contributed by Alander.
 */
void do_visible ( CHAR_DATA * ch, char *argument )
{
  affect_strip ( ch, gsn_sneak );
  xREMOVE_BIT ( ch->affected_by, AFF_HIDE );
  xREMOVE_BIT ( ch->affected_by, AFF_SNEAK );
  send_to_char ( "Ok.\n\r", ch );
  return;
}

void do_recall ( CHAR_DATA * ch, char *argument )
{
  ROOM_INDEX_DATA *location;
  location = NULL;

  if ( xIS_SET ( ch->in_room->room_flags, ROOM_NO_RECALL ) || IS_AFFECTED ( ch, AFF_CURSE ) || ch->position == POS_FIGHTING || ch->in_room == location )
  {
    send_to_char ( "For some strange reason... the God's forbid you from recalling.\n\r", ch );
    return;
  }

  if ( !str_cmp ( argument, "clanhall" ) )
  {
    if ( !IS_NPC ( ch ) && ch->pcdata->clan )
    {
      location = get_room_index ( ch->pcdata->clan->recall );

      if ( location == NULL )
        return;
      else
        char_from_room ( ch );

      char_to_room ( ch, location );

      do_look ( ch, "auto" );

      return;
    }
    else
    {
      send_to_char ( "You do not belong to a clan.\n\r", ch );
      return;
    }
  }

  if ( !str_cmp ( ch->in_room->area->filename, "school.are" ) )
  {
    send_to_char ( "Recalling is FORBIDEN in this location.\n\r", ch );
    return;
  }
  else
  {
    location = get_room_index ( ROOM_VNUM_RECALL );

    if ( IS_PLR_FLAG ( ch, PLR_ONMAP ) || IS_ACT_FLAG ( ch, ACT_ONMAP ) )
    {
      leave_map ( ch, NULL, location );
      return;
    }
  }

  act ( AT_ACTION, "$n disappears in a swirl of smoke.", ch, NULL, NULL, TO_ROOM );

  char_from_room ( ch );
  char_to_room ( ch, location );

  if ( ch->mount )
  {
    char_from_room ( ch->mount );
    char_to_room ( ch->mount, location );
  }

  act ( AT_ACTION, "$n appears in the room.", ch, NULL, NULL, TO_ROOM );

  do_look ( ch, "auto" );
  
  if ( ch->on )
  {
    ch->on = NULL;
    ch->position = POS_STANDING;
  }

  if ( ch->position != POS_STANDING )
  {
    ch->position = POS_STANDING;
  }

  return;
}

void do_mount ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_mount]->skill_level[ch->Class] )
  {
    send_to_char ( "I don't think that would be a good idea...\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You're already mounted!\n\r", ch );
    return;
  }

  if ( ( victim = get_char_room ( ch, argument ) ) == NULL )
  {
    send_to_char ( "You can't find that here.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( victim ) || !xIS_SET ( victim->act, ACT_MOUNTABLE ) )
  {
    send_to_char ( "You can't mount that!\n\r", ch );
    return;
  }

  if ( xIS_SET ( victim->act, ACT_MOUNTED ) )
  {
    send_to_char ( "That mount already has a rider.\n\r", ch );
    return;
  }

  if ( victim->position < POS_STANDING )
  {
    send_to_char ( "Your mount must be standing.\n\r", ch );
    return;
  }

  if ( victim->position == POS_FIGHTING || victim->fighting )
  {
    send_to_char ( "Your mount is moving around too much.\n\r", ch );
    return;
  }

  WAIT_STATE ( ch, skill_table[gsn_mount]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_mount ) )
  {
    xSET_BIT ( victim->act, ACT_MOUNTED );
    ch->mount = victim;
    act ( AT_SKILL, "You mount $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_SKILL, "$n skillfully mounts $N.", ch, NULL, victim, TO_NOTVICT );
    act ( AT_SKILL, "$n mounts you.", ch, NULL, victim, TO_VICT );
    learn_from_success ( ch, gsn_mount );
    ch->position = POS_MOUNTED;
    }
  else
  {
    act ( AT_SKILL, "You unsuccessfully try to mount $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_SKILL, "$n unsuccessfully attempts to mount $N.", ch, NULL, victim, TO_NOTVICT );
    act ( AT_SKILL, "$n tries to mount you.", ch, NULL, victim, TO_VICT );
    learn_from_failure ( ch, gsn_mount );
  }

  return;
}

void do_dismount ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( ( victim = ch->mount ) == NULL )
  {
    send_to_char ( "You're not mounted.\n\r", ch );
    return;
  }

  WAIT_STATE ( ch, skill_table[gsn_mount]->beats );

  if ( can_use_skill ( ch, number_percent(  ), gsn_mount ) )
  {
    act ( AT_SKILL, "You dismount $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_SKILL, "$n skillfully dismounts $N.", ch, NULL, victim, TO_NOTVICT );
    act ( AT_SKILL, "$n dismounts you.  Whew!", ch, NULL, victim, TO_VICT );
    xREMOVE_BIT ( victim->act, ACT_MOUNTED );
    ch->mount = NULL;
    ch->position = POS_STANDING;
    learn_from_success ( ch, gsn_mount );
  }
  else
  {
    act ( AT_SKILL, "You fall off while dismounting $N.  Ouch!", ch, NULL, victim, TO_CHAR );
    act ( AT_SKILL, "$n falls off of $N while dismounting.", ch, NULL, victim, TO_NOTVICT );
    act ( AT_SKILL, "$n falls off your back.", ch, NULL, victim, TO_VICT );
    learn_from_failure ( ch, gsn_mount );
    xREMOVE_BIT ( victim->act, ACT_MOUNTED );
    ch->mount = NULL;
    ch->position = POS_SITTING;
    global_retcode = damage ( ch, ch, 1, TYPE_UNDEFINED );
  }

  return;
}

/*
 * Check for parry.
 */
bool check_parry ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  int chances;

  if ( victim->level < skill_table[gsn_parry]->skill_level[victim->Class] )
    return FALSE;

  if ( !IS_AWAKE ( victim ) )
    return FALSE;

  if ( IS_NPC ( victim ) && !xIS_SET ( victim->defenses, DFND_PARRY ) )
    return FALSE;

  if ( IS_NPC ( victim ) )
  {
    /*
     * Tuan was here.  :)
     */
    chances = UMIN ( 20, victim->level / 1.5 );
  }
  else
  {
    if ( get_eq_char ( victim, WEAR_WIELD ) == NULL )
      return FALSE;

    chances = ( int ) ( LEARNED ( victim, gsn_parry ) / sysdata.parry_mod );
  }

  /*
   * Put in the call to chance() to allow penalties for misaligned
   * clannies.
   */
  if ( !chance ( victim, chances + victim->level - ch->level ) )
  {
    learn_from_failure ( victim, gsn_parry );
    return FALSE;
  }

  if ( !IS_NPC ( victim ) && !IS_SET ( victim->pcdata->flags, PCFLAG_GAG ) )
    /*SB*/
    act ( AT_SKILL, "You parry $n's attack.", ch, NULL, victim, TO_VICT );

  if ( !IS_NPC ( ch ) && !IS_SET ( ch->pcdata->flags, PCFLAG_GAG ) )   /* SB */
    act ( AT_SKILL, "$N parries your attack.", ch, NULL, victim, TO_CHAR );

  learn_from_success ( victim, gsn_parry );

  return TRUE;
}

/*
 * Check for dodge.
 */
bool check_dodge ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  int chances;

  if ( victim->level < skill_table[gsn_dodge]->skill_level[victim->Class] )
    return FALSE;

  if ( !IS_AWAKE ( victim ) )
    return FALSE;

  if ( IS_NPC ( victim ) && !xIS_SET ( victim->defenses, DFND_DODGE ) )
    return FALSE;

  if ( IS_NPC ( victim ) )
    chances = UMIN ( 20, victim->level / 1.5 );
  else
    chances = ( int ) ( LEARNED ( victim, gsn_dodge ) / sysdata.dodge_mod );

  /*
   * Consider luck as a factor
   */
  if ( !chance ( victim, chances + victim->level - ch->level ) )
  {
    learn_from_failure ( victim, gsn_dodge );
    return FALSE;
  }

  if ( !IS_NPC ( victim ) && !IS_SET ( victim->pcdata->flags, PCFLAG_GAG ) )
    act ( AT_SKILL, "You dodge $n's attack.", ch, NULL, victim, TO_VICT );

  if ( !IS_NPC ( ch ) && !IS_SET ( ch->pcdata->flags, PCFLAG_GAG ) )
    act ( AT_SKILL, "$N dodges your attack.", ch, NULL, victim, TO_CHAR );

  learn_from_success ( victim, gsn_dodge );

  return TRUE;
}

bool check_tumble ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  int chances;

  if ( victim->level < skill_table[gsn_tumble]->skill_level[victim->Class] )
    return FALSE;

  if ( !IS_AWAKE ( victim ) )
    return FALSE;

  if ( IS_NPC ( victim ) && !xIS_SET ( victim->defenses, DFND_TUMBLE ) )
    return FALSE;

  if ( IS_NPC ( victim ) )
    chances = UMIN ( 20, victim->level / 1.5 );
  else
    chances = ( int ) ( LEARNED ( victim, gsn_tumble ) / sysdata.tumble_mod + ( get_curr_dex ( victim ) - 13 ) );

  if ( !chance ( victim, chances + victim->level - ch->level ) )
  {
    learn_from_failure ( victim, gsn_tumble );
    return FALSE;
  }

  if ( !IS_NPC ( victim ) && !IS_SET ( victim->pcdata->flags, PCFLAG_GAG ) )
    act ( AT_SKILL, "You tumble away from $n's attack.", ch, NULL, victim, TO_VICT );

  if ( !IS_NPC ( ch ) && !IS_SET ( ch->pcdata->flags, PCFLAG_GAG ) )
    act ( AT_SKILL, "$N tumbles away from your attack.", ch, NULL, victim, TO_CHAR );

  learn_from_success ( victim, gsn_tumble );

  return TRUE;
}

/* Re-worked by Sadiq to allow PCs to scribe up to 3 spells on a single *
 * scroll. Second and third spells progressive increase the chance of   *
 * destroying the scroll.  --Sadiq                                      */
void do_scribe ( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *scroll;
  int sn;
  char buf1[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];
  char buf3[MAX_STRING_LENGTH];
  int mana;

  if ( IS_NPC ( ch ) )
    return;

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_scribe]->skill_level[ch->Class] )
  {
    send_to_char ( "A skill such as this requires more magical ability than that of your Class.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL || !str_cmp ( argument, "" ) )
  {
    send_to_char ( "Scribe what?\n\r", ch );
    return;
  }

  if ( ms_find_obj ( ch ) )
    return;

  if ( ( sn = find_spell ( ch, argument, TRUE ) ) < 0 )
  {
    send_to_char ( "You have not learned that spell.\n\r", ch );
    return;
  }

  if ( skill_table[sn]->spell_fun == spell_null )
  {
    send_to_char ( "That's not a spell!\n\r", ch );
    return;
  }

  if ( SPELL_FLAG ( skill_table[sn], SF_NOSCRIBE ) )
  {
    send_to_char ( "You cannot scribe that spell.\n\r", ch );
    return;
  }

  mana = IS_NPC ( ch ) ? 0 : UMAX ( skill_table[sn]->min_mana, 100 / ( 2 + ch->level - skill_table[sn]->skill_level[ch->Class] ) );

  mana *= 5;

  if ( !IS_NPC ( ch ) && ch->mana < mana )
  {
    send_to_char ( "You don't have enough mana.\n\r", ch );
    return;
  }

  if ( ( scroll = get_eq_char ( ch, WEAR_HOLD ) ) == NULL )
  {
    send_to_char ( "You must be holding a blank scroll to scribe it.\n\r", ch );
    return;
  }

  if ( scroll->pIndexData->vnum != OBJ_VNUM_SCROLL_SCRIBING )
  {
    send_to_char ( "You must be holding a blank scroll to scribe it.\n\r", ch );
    return;
  }

  if ( ( scroll->value[1] != -1 ) && ( scroll->value[2] != -1 ) && ( scroll->value[3] != -1 ) && ( scroll->pIndexData->vnum == OBJ_VNUM_SCROLL_SCRIBING ) )
  {
    send_to_char ( "That scroll has already contains as much magic as it can hold.\n\r", ch );
    return;
  }

  if ( !process_spell_components ( ch, sn ) )
  {
    learn_from_failure ( ch, gsn_scribe );
    ch->mana -= ( mana / 2 );
    return;
  }

  if ( !IS_NPC ( ch ) && number_percent(  ) > ch->pcdata->learned[gsn_scribe] )
  {
    set_char_color ( AT_MAGIC, ch );
    send_to_char ( "The magic surges outof control and destroys the scroll!.\n\r", ch );
    learn_from_failure ( ch, gsn_scribe );
    ch->mana -= ( mana / 2 );
    extract_obj ( scroll );
    return;
  }

  if ( scroll->value[1] == -1 )
  {
    scroll->value[1] = sn;
    scroll->value[0] = ch->level;
    sprintf ( buf1, "magically scribed scroll" );
    STRFREE ( scroll->short_descr );
    scroll->short_descr = STRALLOC ( aoran ( buf1 ) );
    sprintf ( buf2, "A magically scribed scroll lies in the dust." );
    STRFREE ( scroll->description );
    scroll->description = STRALLOC ( buf2 );
    sprintf ( buf3, "scroll scribing %s", skill_table[sn]->name );
    STRFREE ( scroll->name );
    scroll->name = STRALLOC ( buf3 );
    act ( AT_MAGIC, "$n magiclly scribes a scroll.", ch, NULL, NULL, TO_ROOM );
    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the scroll with %s.\n\r", skill_table[sn]->name );
    /*
     * act( AT_MAGIC, "$n magically scribes a scroll.",   ch, NULL, NULL, TO_ROOM );
     */
    /*
     * act( AT_MAGIC, "You magically scribe $p.",   ch, NULL, NULL, TO_CHAR );
     */
    learn_from_success ( ch, gsn_scribe );
    ch->mana -= mana;
    return;
  }

  if ( scroll->value[2] == -1 )
  {
    if ( number_percent(  ) > 80 )
    {
      set_char_color ( AT_MAGIC, ch );
      send_to_char ( "The magic surges out of control and destroys the scroll!.\n\r", ch );
      learn_from_failure ( ch, gsn_scribe );
      ch->mana -= ( mana / 2 );
      extract_obj ( scroll );
      return;
    }

    if ( scroll->value[0] > ch->level )
    {
      scroll->value[0] = ch->level;
    }

    scroll->value[2] = sn;

    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the scroll with %s.\n\r", skill_table[sn]->name );
    learn_from_success ( ch, gsn_scribe );
    ch->mana -= mana;
    return;
  }

  if ( scroll->value[3] == -1 )
  {
    if ( number_percent(  ) > 60 )
    {
      set_char_color ( AT_MAGIC, ch );
      send_to_char ( "The magic surges outof control and destroys the scroll!.\n\r", ch );
      learn_from_failure ( ch, gsn_scribe );
      ch->mana -= ( mana / 2 );
      extract_obj ( scroll );
      return;
    }

    if ( scroll->value[0] > ch->level )
    {
      scroll->value[0] = ch->level;
    }

    scroll->value[3] = sn;

    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the scroll with %s.\n\r", skill_table[sn]->name );
    learn_from_success ( ch, gsn_scribe );
    ch->mana -= mana;
    return;
  }
}

/*
brewing 3 spells to one vial, based on do_scribe 3 spells by sadiq
*/
void do_brew ( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *potion;
  int sn;
  char buf1[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];
  char buf3[MAX_STRING_LENGTH];
  int mana;

  if ( IS_NPC ( ch ) )
    return;

  if ( !IS_NPC ( ch ) && ch->level < skill_table[gsn_brew]->skill_level[ch->Class] )
  {
    send_to_char ( "A skill such as this requires more magical ability than that of your Class.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL || !str_cmp ( argument, "" ) )
  {
    send_to_char ( "brew what?\n\r", ch );
    return;
  }

  if ( ms_find_obj ( ch ) )
    return;

  if ( ( sn = find_spell ( ch, argument, TRUE ) ) < 0 )
  {
    send_to_char ( "You have not learned that spell.\n\r", ch );
    return;
  }

  if ( skill_table[sn]->spell_fun == spell_null )
  {
    send_to_char ( "That's not a spell!\n\r", ch );
    return;
  }

  if ( SPELL_FLAG ( skill_table[sn], SF_NOBREW ) )
  {
    send_to_char ( "You cannot brew that spell.\n\r", ch );
    return;
  }

  mana = IS_NPC ( ch ) ? 0 : UMAX ( skill_table[sn]->min_mana, 100 / ( 2 + ch->level - skill_table[sn]->skill_level[ch->Class] ) );

  mana *= 5;

  if ( !IS_NPC ( ch ) && ch->mana < mana )
  {
    send_to_char ( "You don't have enough mana.\n\r", ch );
    return;
  }

  if ( ( potion = get_eq_char ( ch, WEAR_HOLD ) ) == NULL )
  {
    send_to_char ( "You must be holding a blank potion to brew it.\n\r", ch );
    return;
  }

  if ( potion->pIndexData->vnum != OBJ_VNUM_FLASK_BREWING )
  {
    send_to_char ( "You must be holding a blank potion to brew it.\n\r", ch );
    return;
  }

  if ( ( potion->value[1] != -1 ) && ( potion->value[2] != -1 ) && ( potion->value[3] != -1 ) && ( potion->pIndexData->vnum == OBJ_VNUM_FLASK_BREWING ) )
  {
    send_to_char ( "That potion has already contains as much magic as it can hold.\n\r", ch );
    return;
  }

  if ( !process_spell_components ( ch, sn ) )
  {
    learn_from_failure ( ch, gsn_brew );
    ch->mana -= ( mana / 2 );
    return;
  }

  if ( !IS_NPC ( ch ) && number_percent(  ) > ch->pcdata->learned[gsn_brew] )
  {
    set_char_color ( AT_MAGIC, ch );
    send_to_char ( "The magic surges outof control and destroys the potion!.\n\r", ch );
    learn_from_failure ( ch, gsn_brew );
    ch->mana -= ( mana / 2 );
    extract_obj ( potion );
    return;
  }

  if ( potion->value[1] == -1 )
  {
    potion->value[1] = sn;
    potion->value[0] = ch->level;
    sprintf ( buf1, "A magically brewed potion %s", skill_table[sn]->name );
    STRFREE ( potion->short_descr );
    potion->short_descr = STRALLOC ( aoran ( buf1 ) );
    sprintf ( buf2, "A magically brewed potion lies in the dust." );
    STRFREE ( potion->description );
    potion->description = STRALLOC ( buf2 );
    sprintf ( buf3, "potion scribing %s", skill_table[sn]->name );
    STRFREE ( potion->name );
    potion->name = STRALLOC ( buf3 );
    act ( AT_MAGIC, "$n magiclly brews a potion.", ch, NULL, NULL, TO_ROOM );
    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the potion with %s.\n\r", skill_table[sn]->name );
    learn_from_success ( ch, gsn_brew );
    ch->mana -= mana;
    return;
  }

  if ( potion->value[2] == -1 )
  {
    if ( number_percent(  ) > 80 )
    {
      set_char_color ( AT_MAGIC, ch );
      send_to_char ( "The magic surges out of control and destroys the potion!.\n\r", ch );
      learn_from_failure ( ch, gsn_brew );
      ch->mana -= ( mana / 2 );
      extract_obj ( potion );
      return;
    }

    if ( potion->value[0] > ch->level )
    {
      potion->value[0] = ch->level;
    }

    potion->value[2] = sn;

    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the potion with %s.\n\r", skill_table[sn]->name );
    learn_from_success ( ch, gsn_brew );
    ch->mana -= mana;
    return;
  }

  if ( potion->value[3] == -1 )
  {
    if ( number_percent(  ) < 20 )
    {
      set_char_color ( AT_MAGIC, ch );
      send_to_char ( "The magic surges outof control and destroys the potion!.\n\r", ch );
      learn_from_failure ( ch, gsn_brew );
      ch->mana -= ( mana / 2 );
      extract_obj ( potion );
      return;
    }

    if ( potion->value[0] > ch->level )
    {
      potion->value[0] = ch->level;
    }

    potion->value[3] = sn;

    set_char_color ( AT_MAGIC, ch );
    ch_printf ( ch, "You imbue the potion with %s.\n\r", skill_table[sn]->name );
    learn_from_success ( ch, gsn_brew );
    ch->mana -= mana;
    return;
  }
}

bool check_grip ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  int schance;

  if ( !IS_AWAKE ( victim ) )
    return FALSE;

  if ( IS_NPC ( victim ) && !xIS_SET ( victim->defenses, DFND_GRIP ) )
    return FALSE;

  if ( IS_NPC ( victim ) )
    schance = UMIN ( 20, victim->level / 1.5 );
  else
    schance = ( LEARNED ( victim, gsn_grip ) );

  /*
   * Consider luck as a factor
   */
  schance += ( 2 * ( get_curr_lck ( victim ) - 13 ) );

  if ( number_percent(  ) >= schance + victim->level - ch->level )
  {
    learn_from_failure ( victim, gsn_grip );
    return FALSE;
  }
  else
  {
    act ( AT_SKILL, "You evade $n's attempt to disarm you.", ch, NULL, victim, TO_VICT );
    act ( AT_SKILL, "$N holds $S weapon strongly, and is not disarmed.", ch, NULL, victim, TO_CHAR );
    learn_from_success ( victim, gsn_grip );
    return TRUE;
  }
}

void do_circle ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  OBJ_DATA *obj;
  int percent;

  if ( IS_NPC ( ch ) && IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  one_argument ( argument, arg );

  if ( ch->mount )
  {
    send_to_char ( "You can't circle while mounted.\n\r", ch );
    return;
  }

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Circle around whom?\n\r", ch );
    return;
  }

  if ( ( victim = get_char_room ( ch, arg ) ) == NULL )
  {
    send_to_char ( "They aren't here.\n\r", ch );
    return;
  }

  if ( victim == ch )
  {
    send_to_char ( "How can you sneak up on yourself?\n\r", ch );
    return;
  }

  if ( is_safe ( ch, victim, TRUE ) )
    return;

  if ( ! ( obj = get_eq_char ( ch, WEAR_WIELD ) ) )
  {
    send_to_char ( "You need to wield a piercing weapon.\n\r", ch );
    return;
  }

  if ( obj->value[3] != DAM_PIERCE )
  {
    send_to_char ( "You need to wield a piercing weapon to circle.\n\r", ch );
    return;
  }

  if ( !ch->fighting )
  {
    send_to_char ( "You can't circle when you aren't fighting.\n\r", ch );
    return;
  }

  if ( !victim->fighting )
  {
    send_to_char ( "You can't circle around a person who is not fighting.\n\r", ch );
    return;
  }

  if ( victim->num_fighting < 2 )
  {
    act ( AT_PLAIN, "You can't circle around them without a distraction.", ch, NULL, victim, TO_CHAR );
    return;
  }

  percent = number_percent(  ) - ( get_curr_lck ( ch ) - 16 ) + ( get_curr_lck ( victim ) - 13 );

  check_attacker ( ch, victim );
  WAIT_STATE ( ch, skill_table[gsn_circle]->beats );

  if ( can_use_skill ( ch, percent, gsn_circle ) )
  {
    learn_from_success ( ch, gsn_circle );
    act ( AT_RED, "You circle around $N.", ch, NULL, victim, TO_CHAR );
    act ( AT_RED, "$N gets cut to ribbons by $n's circle.", ch, NULL, victim, TO_ROOM );
    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );
    global_retcode = multi_hit ( ch, victim, gsn_circle );
    adjust_favor ( ch, 10, 1 );
    check_illegal_pk ( ch, victim );
  }
  else
  {
    learn_from_failure ( ch, gsn_circle );
    act ( AT_RED, "Your too slow to circle $N.", ch, NULL, victim, TO_CHAR );
    WAIT_STATE ( ch, 2 * PULSE_VIOLENCE );
    global_retcode = damage ( ch, victim, 0, gsn_circle );
  }

  return;
}

/* Berserk and HitAll. -- Altrag */
void do_berserk ( CHAR_DATA * ch, char *argument )
{
  short percent;
  AFFECT_DATA af;

  if ( !ch->fighting )
  {
    send_to_char ( "But you aren't fighting!\n\r", ch );
    return;
  }

  if ( IS_AFFECTED ( ch, AFF_BERSERK ) )
  {
    send_to_char ( "Your rage is already at its peak!\n\r", ch );
    return;
  }

  percent = LEARNED ( ch, gsn_berserk );

  WAIT_STATE ( ch, skill_table[gsn_berserk]->beats );

  if ( !chance ( ch, percent ) )
  {
    send_to_char ( "You couldn't build up enough rage.\n\r", ch );
    learn_from_failure ( ch, gsn_berserk );
    return;
  }

  af.type = gsn_berserk;

  /*
   * Hmmm.. 10-20 combat rounds at level 50.. good enough for most mobs,
   * and if not they can always go berserk again.. shrug.. maybe even
   * too high. -- Altrag
   */
  af.duration = number_range ( ch->level / 5, ch->level * 2 / 5 );
  /*
   * Hmm.. you get stronger when yer really enraged.. mind over matter
   * type thing..
   */
  af.location = APPLY_STR;
  af.modifier = 1;
  af.bitvector = meb ( AFF_BERSERK );
  affect_to_char ( ch, &af );
  send_to_char ( "You start to lose control..\n\r", ch );
  learn_from_success ( ch, gsn_berserk );
  return;
}

bool check_illegal_psteal ( CHAR_DATA * ch, CHAR_DATA * victim )
{
  if ( !IS_NPC ( victim ) && !IS_NPC ( ch ) )
  {
    if ( ( !IS_SET ( victim->pcdata->flags, PCFLAG_DEADLY )
           || ch->level - victim->level > 10 || !IS_SET ( ch->pcdata->flags, PCFLAG_DEADLY ) ) && ( ch->in_room->vnum < 29 || ch->in_room->vnum > 43 ) && ch != victim )
    {
      /*
       * sprintf( log_buf, "%s illegally stealing from %s at %d",
       * (IS_NPC(ch) ? ch->short_descr : ch->name),
       * victim->name,
       * victim->in_room->vnum );
       * log_string( log_buf );
       * to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
       */
      return TRUE;
    }
  }

  return FALSE;
}

static char *dir_desc[] =
{
  "to the north",
  "to the east",
  "to the south",
  "to the west",
  "upwards",
  "downwards",
  "to the northeast",
  "to the northwest",
  "to the southeast",
  "to the southwest",
  "through the portal"
};
static char *rng_desc[] =
{
  "right here",
  "immediately",
  "nearby",
  "a ways",
  "a good ways",
  "far",
  "far off",
  "very far",
  "very far off",
  "in the distance"
};
static void scanroom ( CHAR_DATA * ch, ROOM_INDEX_DATA * room, int dir, int maxdist, int dist )
{
  CHAR_DATA *tch;
  EXIT_DATA *ex;

  for ( tch = room->first_person; tch; tch = tch->next_in_room )
  {
    if ( can_see ( ch, tch, FALSE ) && !is_ignoring ( tch, ch ) )
      ch_printf ( ch, "%-30s : %s %s\n\r", IS_NPC ( tch ) ? tch->short_descr : tch->name, rng_desc[dist], dist == 0 ? "" : dir_desc[dir] );
  }

  for ( ex = room->first_exit; ex; ex = ex->next )
    if ( ex->vdir == dir )
      break;

  if ( !ex || ex->vdir != dir || ex->vdir == DIR_SOMEWHERE || maxdist - 1 == 0 || IS_EXIT_FLAG ( ex, EX_CLOSED ) || IS_EXIT_FLAG ( ex, EX_DIG )   /* || IS_EXIT_FLAG(ex, EX_FORTIFIED)
                                                                                                                                                 * || IS_EXIT_FLAG(ex, EX_HEAVY) || IS_EXIT_FLAG(ex, EX_MEDIUM) || IS_EXIT_FLAG(ex, EX_LIGHT)
                                                                                                                                                 * || IS_EXIT_FLAG(ex, EX_CRUMBLING) */  || IS_EXIT_FLAG ( ex, EX_OVERLAND ) )
    return;

  scanroom ( ch, ex->to_room, dir, maxdist - 1, dist + 1 );
}

void map_scan ( CHAR_DATA * ch );
/* Scan no longer accepts a direction argument */
void do_scan ( CHAR_DATA * ch, char *argument )
{
  int maxdist = 1;
  EXIT_DATA *ex;
  maxdist = ch->level / 10;
  maxdist = URANGE ( 1, maxdist, 9 );

  if ( IS_PLR_FLAG ( ch, PLR_ONMAP ) || IS_ACT_FLAG ( ch, ACT_ONMAP ) )
  {
    map_scan ( ch );
    return;
  }

  scanroom ( ch, ch->in_room, -1, 1, 0 );

  for ( ex = ch->in_room->first_exit; ex; ex = ex->next )
  {
    if ( IS_EXIT_FLAG ( ex, EX_DIG ) || IS_EXIT_FLAG ( ex, EX_CLOSED )
         /*
          * ||  IS_EXIT_FLAG(ex, EX_FORTIFIED) || IS_EXIT_FLAG(ex, EX_HEAVY)
          * ||  IS_EXIT_FLAG(ex, EX_MEDIUM) || IS_EXIT_FLAG(ex, EX_LIGHT)
          * ||  IS_EXIT_FLAG(ex, EX_CRUMBLING)
          */  || IS_EXIT_FLAG ( ex, EX_OVERLAND ) )
      continue;

    if ( ex->vdir == DIR_SOMEWHERE && !IS_IMMORTAL ( ch ) )
      continue;

    scanroom ( ch, ex->to_room, ex->vdir, maxdist, 1 );

    learn_from_success ( ch, gsn_scan );
  }
}

/* -- working on --
 * Syntaxes: throw object  (assumed already fighting)
 *      throw object direction target  (all needed args for distance
 *           throwing)
 *      throw object  (assumed same room throw)
void do_throw( CHAR_DATA *ch, char *argument )
{
  ROOM_INDEX_DATA *was_in_room;
  CHAR_DATA *victim;
  OBJ_DATA *throw_obj;
  EXIT_DATA *pexit;
  short dir;
  short dist;
  short max_dist = 3;
  char arg[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  argument = one_argument( argument, arg );
  argument = one_argument( argument, arg1 );
  argument = one_argument( argument, arg2 );
  for ( throw_obj = ch->last_carrying; throw_obj;
 throw_obj = throw_obj=>prev_content )
  {
---    if ( can_see_obj( ch, throw_obj )
 && ( throw_obj->wear_loc == WEAR_HELD || throw_obj->wear_loc ==
 WEAR_WIELDED || throw_obj->wear_loc == WEAR_DUAL_WIELDED )
 && nifty_is_name( arg, throw_obj->name ) )
      break;
 ----
    if ( can_see_obj( ch, throw_obj ) && nifty_is_name( arg, throw_obj->name )
      break;
  }
  if ( !throw_obj )
  {
    send_to_char( "You aren't holding or wielding anything like that.\n\r", ch );
    return;
  }
----
  if ( ( throw_obj->item_type != ITEM_WEAPON)
  {
    send_to_char("You can only throw weapons.\n\r", ch );
    return;
  }
----
  if (get_obj_weight( throw_obj ) - ( 3 * (get_curr_str(ch) - 15) ) > 0)
  {
    send_to_char("That is too heavy for you to throw.\n\r", ch);
    if (!number_range(0,10))
      learn_from_failure( ch, gsn_throw );
    return;
  }
  if ( ch->fighting )
    victim = ch->fighting;
   else
    {
      if ( ( ( victim = get_char_room( ch, arg1 ) ) == NULL )
   && ( arg2[0] == STRING_NULL ) )
      {
        act( AT_GREY, "Throw $t at whom?", ch, obj->short_descr, NULL,
   TO_CHAR );
        return;
      }
    }
}*/
void do_slice ( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *corpse;
  OBJ_DATA *obj;
  OBJ_DATA *slice;
  bool found;
  char buf[MAX_STRING_LENGTH];
  char buf1[MAX_STRING_LENGTH];
  found = FALSE;
  /*
   * Noticed that it was checking gsn_kick.  Bug report by Li'l Lukey
   */

  if ( !IS_NPC ( ch ) && !IS_IMMORTAL ( ch ) && ch->level < skill_table[gsn_slice]->skill_level[ch->Class] )
  {
    send_to_char ( "You are not learned in this skill.\n\r", ch );
    return;
  }

  if ( argument[0] == STRING_NULL )
  {
    send_to_char ( "From what do you wish to slice meat?\n\r", ch );
    return;
  }

  if ( ( obj = get_eq_char ( ch, WEAR_WIELD ) ) == NULL || ( obj->value[3] != 1 && obj->value[3] != 2 && obj->value[3] != 3 && obj->value[3] != 11 ) )
  {
    send_to_char ( "You need to wield a sharp weapon.\n\r", ch );
    return;
  }

  if ( ( corpse = get_obj_here ( ch, argument ) ) == NULL )
  {
    send_to_char ( "You can't find that here.\n\r", ch );
    return;
  }

  if ( corpse->item_type != ITEM_CORPSE_NPC )
  {
    send_to_char ( "That is not a suitable source of meat.\n\r", ch );
    return;
  }

  if ( get_obj_index ( OBJ_VNUM_SLICE ) == NULL )
  {
    bug ( "Vnum 24 not found for do_slice!", 0 );
    return;
  }

  if ( !can_use_skill ( ch, number_percent(  ), gsn_slice ) && !IS_IMMORTAL ( ch ) )
  {
    send_to_char ( "You fail to slice the meat properly.\n\r", ch );
    learn_from_failure ( ch, gsn_slice );  /* Just in case they die :> */

    if ( number_percent(  ) + ( get_curr_dex ( ch ) - 13 ) < 10 )
    {
      act ( AT_BLOOD, "You cut yourself!", ch, NULL, NULL, TO_CHAR );
      damage ( ch, ch, ch->level, gsn_slice );
    }

    return;
  }

  slice = create_object ( get_obj_index ( OBJ_VNUM_SLICE ), 0 );

  sprintf ( buf, "meat fresh slice %s", corpse->name );
  STRFREE ( slice->name );
  slice->name = STRALLOC ( buf );
  sprintf ( buf, "a slice of raw meat from %s", corpse->short_descr );
  STRFREE ( slice->short_descr );
  slice->short_descr = STRALLOC ( buf );
  sprintf ( buf1, "A slice of raw meat from %s lies on the ground.", corpse->short_descr );
  STRFREE ( slice->description );
  slice->description = STRALLOC ( buf1 );
  act ( AT_BLOOD, "$n cuts a slice of meat from $p.", ch, corpse, NULL, TO_ROOM );
  act ( AT_BLOOD, "You cut a slice of meat from $p.", ch, corpse, NULL, TO_CHAR );
  slice->value[3] = 0;
  slice->value[2] = 0;
  obj_to_char ( slice, ch );
  obj_from_room ( corpse );
  learn_from_success ( ch, gsn_slice );
  return;
}

/*------------------------------------------------------------
 *  Fighting Styles - haus
 */
void do_style ( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  /*
   * char buf[MAX_INPUT_LENGTH];
   * int percent;
   */

  if ( IS_NPC ( ch ) )
    return;

  one_argument ( argument, arg );

  if ( arg[0] == STRING_NULL )
  {
    ch_printf_color ( ch, "&wAdopt which fighting style?  (current:  %s&w)\n\r",
                      ch->style == STYLE_BERSERK ? "&Rberserk" :
                      ch->style == STYLE_AGGRESSIVE ? "&Raggressive" :
                      ch->style == STYLE_DEFENSIVE ? "&Ydefensive" :
                      ch->style == STYLE_EVASIVE ? "&Yevasive" : "standard" );
    return;
  }

  if ( !str_prefix ( arg, "evasive" ) )
  {
    if ( ch->level < skill_table[gsn_style_evasive]->skill_level[ch->Class] )
    {
      send_to_char ( "You have not yet learned enough to fight evasively.\n\r", ch );
      return;
    }

    WAIT_STATE ( ch, skill_table[gsn_style_evasive]->beats );

    if ( number_percent(  ) < LEARNED ( ch, gsn_style_evasive ) )
    {
      /*
       * success
       */
      if ( ch->fighting )
      {
        ch->position = POS_EVASIVE;
        learn_from_success ( ch, gsn_style_evasive );

        if ( IS_PKILL ( ch ) )
          act ( AT_ACTION, "$n falls back into an evasive stance.", ch, NULL, NULL, TO_ROOM );
      }

      ch->style = STYLE_EVASIVE;

      send_to_char ( "You adopt an evasive fighting style.\n\r", ch );
      return;
    }
    else
    {
      /*
       * failure
       */
      send_to_char ( "You nearly trip in a lame attempt to adopt an evasive fighting style.\n\r", ch );
      learn_from_failure ( ch, gsn_style_evasive );
      return;
    }
  }
  else
    if ( !str_prefix ( arg, "defensive" ) )
    {
      if ( ch->level < skill_table[gsn_style_defensive]->skill_level[ch->Class] )
      {
        send_to_char ( "You have not yet learned enough to fight defensively.\n\r", ch );
        return;
      }

      WAIT_STATE ( ch, skill_table[gsn_style_defensive]->beats );

      if ( number_percent(  ) < LEARNED ( ch, gsn_style_defensive ) )
      {
        /*
         * success
         */
        if ( ch->fighting )
        {
          ch->position = POS_DEFENSIVE;
          learn_from_success ( ch, gsn_style_defensive );

          if ( IS_PKILL ( ch ) )
            act ( AT_ACTION, "$n moves into a defensive posture.", ch, NULL, NULL, TO_ROOM );
        }

        ch->style = STYLE_DEFENSIVE;

        send_to_char ( "You adopt a defensive fighting style.\n\r", ch );
        return;
      }
      else
      {
        /*
         * failure
         */
        send_to_char ( "You nearly trip in a lame attempt to adopt a defensive fighting style.\n\r", ch );
        learn_from_failure ( ch, gsn_style_defensive );
        return;
      }
    }
    else
      if ( !str_prefix ( arg, "standard" ) )
      {
        if ( ch->level < skill_table[gsn_style_standard]->skill_level[ch->Class] )
        {
          send_to_char ( "You have not yet learned enough to fight in the standard style.\n\r", ch );
          return;
        }

        WAIT_STATE ( ch, skill_table[gsn_style_standard]->beats );

        if ( number_percent(  ) < LEARNED ( ch, gsn_style_standard ) )
        {
          /*
           * success
           */
          if ( ch->fighting )
          {
            ch->position = POS_FIGHTING;
            learn_from_success ( ch, gsn_style_standard );

            if ( IS_PKILL ( ch ) )
              act ( AT_ACTION, "$n switches to a standard fighting style.", ch, NULL, NULL, TO_ROOM );
          }

          ch->style = STYLE_FIGHTING;

          send_to_char ( "You adopt a standard fighting style.\n\r", ch );
          return;
        }
        else
        {
          /*
           * failure
           */
          send_to_char ( "You nearly trip in a lame attempt to adopt a standard fighting style.\n\r", ch );
          learn_from_failure ( ch, gsn_style_standard );
          return;
        }
      }
      else
        if ( !str_prefix ( arg, "aggressive" ) )
        {
          if ( ch->level < skill_table[gsn_style_aggressive]->skill_level[ch->Class] )
          {
            send_to_char ( "You have not yet learned enough to fight aggressively.\n\r", ch );
            return;
          }

          WAIT_STATE ( ch, skill_table[gsn_style_aggressive]->beats );

          if ( number_percent(  ) < LEARNED ( ch, gsn_style_aggressive ) )
          {
            /*
             * success
             */
            if ( ch->fighting )
            {
              ch->position = POS_AGGRESSIVE;
              learn_from_success ( ch, gsn_style_aggressive );

              if ( IS_PKILL ( ch ) )
                act ( AT_ACTION, "$n assumes an aggressive stance.", ch, NULL, NULL, TO_ROOM );
            }

            ch->style = STYLE_AGGRESSIVE;

            send_to_char ( "You adopt an aggressive fighting style.\n\r", ch );
            return;
          }
          else
          {
            /*
             * failure
             */
            send_to_char ( "You nearly trip in a lame attempt to adopt an aggressive fighting style.\n\r", ch );
            learn_from_failure ( ch, gsn_style_aggressive );
            return;
          }
        }
        else
          if ( !str_prefix ( arg, "berserk" ) )
          {
            if ( ch->level < skill_table[gsn_style_berserk]->skill_level[ch->Class] )
            {
              send_to_char ( "You have not yet learned enough to fight as a berserker.\n\r", ch );
              return;
            }

            WAIT_STATE ( ch, skill_table[gsn_style_berserk]->beats );

            if ( number_percent(  ) < LEARNED ( ch, gsn_style_berserk ) )
            {
              /*
               * success
               */
              if ( ch->fighting )
              {
                ch->position = POS_BERSERK;
                learn_from_success ( ch, gsn_style_berserk );

                if ( IS_PKILL ( ch ) )
                  act ( AT_ACTION, "$n enters a wildly aggressive style.", ch, NULL, NULL, TO_ROOM );
              }

              ch->style = STYLE_BERSERK;

              send_to_char ( "You adopt a berserk fighting style.\n\r", ch );
              return;
            }
            else
            {
              /*
               * failure
               */
              send_to_char ( "You nearly trip in a lame attempt to adopt a berserk fighting style.\n\r", ch );
              learn_from_failure ( ch, gsn_style_berserk );
              return;
            }
          }

  send_to_char ( "Adopt which fighting style?\n\r", ch );

  return;
}

bool can_use_skill ( CHAR_DATA * ch, int percent, int gsn )
{
  bool check = FALSE;

  if ( IS_NPC ( ch ) && percent < 85 )
    check = TRUE;
  else
    if ( !IS_NPC ( ch ) && percent < LEARNED ( ch, gsn ) )
      check = TRUE;

  return check;
}

/*
 * Cook was coded by Blackmane and heavily modified by Shaddai
 */
void do_cook ( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *food, *fire;
  char arg[MAX_INPUT_LENGTH];
  char buf[MAX_STRING_LENGTH];
  one_argument ( argument, arg );

  if ( IS_NPC ( ch ) || ch->level < skill_table[gsn_cook]->skill_level[ch->Class] )
  {
    send_to_char ( "That skill is beyond your understanding.\n\r", ch );
    return;
  }

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Cook what?\n\r", ch );
    return;
  }

  if ( ms_find_obj ( ch ) )
    return;

  if ( ( food = get_obj_carry ( ch, arg ) ) == NULL )
  {
    send_to_char ( "You do not have that item.\n\r", ch );
    return;
  }

  if ( food->item_type != ITEM_COOK )
  {
    send_to_char ( "How can you cook that?\n\r", ch );
    return;
  }

  if ( food->value[2] > 2 )
  {
    send_to_char ( "That is already burnt to a crisp.\n\r", ch );
    return;
  }

  for ( fire = ch->in_room->first_content; fire; fire = fire->next_content )
  {
    if ( fire->item_type == ITEM_FIRE )
      break;
  }

  if ( !fire )
  {
    send_to_char ( "There is no fire here!\n\r", ch );
    return;
  }

  separate_obj ( food );  /* Bug catch by Tchaika from SMAUG list */

  if ( number_percent(  ) > LEARNED ( ch, gsn_cook ) )
  {
    food->timer = food->timer / 2;
    food->value[0] = 0;
    food->value[2] = 3;
    act ( AT_MAGIC, "$p catches on fire burning it to a crisp!\n\r", ch, food, NULL, TO_CHAR );
    act ( AT_MAGIC, "$n catches $p on fire burning it to a crisp.", ch, food, NULL, TO_ROOM );
    sprintf ( buf, "a burnt %s", food->pIndexData->name );
    STRFREE ( food->short_descr );
    food->short_descr = STRALLOC ( buf );
    sprintf ( buf, "A burnt %s.", food->pIndexData->name );
    STRFREE ( food->description );
    food->description = STRALLOC ( buf );
    return;
  }

  if ( number_percent(  ) > 85 )
  {
    food->timer = food->timer * 3;
    food->value[2] += 2;
    act ( AT_MAGIC, "$n overcooks a $p.", ch, food, NULL, TO_ROOM );
    act ( AT_MAGIC, "You overcook a $p.", ch, food, NULL, TO_CHAR );
    sprintf ( buf, "an overcooked %s", food->pIndexData->name );
    STRFREE ( food->short_descr );
    food->short_descr = STRALLOC ( buf );
    sprintf ( buf, "An overcooked %s.", food->pIndexData->name );
    STRFREE ( food->description );
    food->description = STRALLOC ( buf );
  }
  else
  {
    food->timer = food->timer * 4;
    food->value[0] *= 2;
    act ( AT_MAGIC, "$n roasts a $p.", ch, food, NULL, TO_ROOM );
    act ( AT_MAGIC, "You roast a $p.", ch, food, NULL, TO_CHAR );
    sprintf ( buf, "a roasted %s", food->pIndexData->name );
    STRFREE ( food->short_descr );
    food->short_descr = STRALLOC ( buf );
    sprintf ( buf, "A roasted %s.", food->pIndexData->name );
    STRFREE ( food->description );
    food->description = STRALLOC ( buf );
    food->value[2]++;
  }

  learn_from_success ( ch, gsn_cook );
}

void do_meditate ( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;

  if ( IS_NPC ( ch ) )
  {
    send_to_char ( "Mobs cannot use this skill.\n\r", ch );
    return;
  }

  if ( IS_AFFECTED ( ch, AFF_CHARM ) )
  {
    send_to_char ( "You can't concentrate enough for that.\n\r", ch );
    return;
  }

  if ( ch->mount )
  {
    send_to_char ( "You can't do that while mounted.\n\r", ch );
    return;
  }

  if ( xIS_SET ( ch->in_room->room_flags, ROOM_NO_MAGIC ) )
  {
    set_char_color ( AT_MAGIC, ch );
    send_to_char ( "You failed.\n\r", ch );
    return;
  }

  if ( !IS_NPC ( ch ) && number_percent(  ) > ch->pcdata->learned[gsn_meditate] )
  {
    act ( AT_MAGIC, "You get distracted and stop meditating.", ch, NULL, victim, TO_CHAR );
    act ( AT_MAGIC, "$n is distracted by a vuage memory and stops meditating.", ch, NULL, victim, TO_ROOM );
    learn_from_failure ( ch, gsn_meditate );
    return;
  }
  else
  {
    act ( AT_MAGIC, "You meditate peacefully, collecting mana from the cosmos.", ch, NULL, victim, TO_CHAR );
    act ( AT_MAGIC, "$n meditates peacefully, collecting mana from the cosmos.", ch, NULL, victim, TO_ROOM );
    ch->mana += ( ch->level / 2 + 20 );
    learn_from_success ( ch, gsn_meditate );

    if ( ch->mana > ch->max_mana )
      ch->mana = ch->max_mana;

    return;
  }
}

/* Allows PCs to learn spells embedded in object. Should prove interesting. - Samson 8-9-98 */
void do_study ( CHAR_DATA * ch, char *argument ) /* study by Absalom */
{
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj;
  int sn = 0;
  one_argument ( argument, arg );

  if ( arg[0] == STRING_NULL )
  {
    send_to_char ( "Study what?\n\r", ch );
    return;
  }

  if ( ( obj = get_obj_carry ( ch, arg ) ) == NULL )
  {
    send_to_char ( "You do not have that item.\n\r", ch );
    return;
  }

  if ( obj->pIndexData->vnum == 34 )
  {
    send_to_char ( "You cannot study a scribed scroll.\n\r", ch );
    return;
  }

  if ( obj->item_type != ITEM_SCROLL )
  {
    send_to_char ( "You can only study scrolls.\n\r", ch );
    return;
  }

  act ( AT_MAGIC, "$n studies $p.", ch, obj, NULL, TO_ROOM );
  act ( AT_MAGIC, "You study $p.", ch, obj, NULL, TO_CHAR );
  sn = obj->value[1];

  if ( sn < 0 || sn >= MAX_SKILL || skill_table[sn]->spell_fun == spell_null )
  {
    bug ( "Do_study: bad sn %d.", sn );
    return;
  }

  if ( number_percent(  ) >= 15 + ch->pcdata->learned[gsn_study] * 4 / 5 )
  {
    send_to_char ( "You cannot glean any knowledge from it.\n\r", ch );
    learn_from_failure ( ch, gsn_study );
    act ( AT_FIRE, "$p burns brightly and is gone.", ch, obj, NULL, TO_CHAR );
    separate_obj ( obj );
    extract_obj ( obj );
    return;
  }

  if ( ch->pcdata->learned[sn] )
  {
    send_to_char ( "You already know that spell!\n\r", ch );
    return;
  }

  ch->pcdata->learned[sn] = 1;

  act ( AT_MAGIC, "You have learned the ancient art of $t!", ch, skill_table[sn]->name, NULL, TO_CHAR );
  learn_from_success ( ch, gsn_study );
  act ( AT_FIRE, "$p burns brightly and is gone.", ch, obj, NULL, TO_CHAR );
  separate_obj ( obj );
  extract_obj ( obj );
  return;

}