swr2.0/
swr2.0/area/
swr2.0/boards/
swr2.0/clans/
swr2.0/doc/
swr2.0/planets/
swr2.0/spacecraft/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mud.h"

void show_char_to_char( CHAR_DATA * list, CHAR_DATA * ch );
int ris_save( CHAR_DATA * ch, int chance, int ris );
bool check_illegal_psteal( CHAR_DATA * ch, CHAR_DATA * victim );

/* from magic.c */
void failed_casting( SKILLTYPE *skill, CHAR_DATA * ch,
    CHAR_DATA * victim, OBJ_DATA * obj );

int character_skill_level( const CHAR_DATA * ch, short skill )
{
  return IS_NPC( ch ) ? 100 : ( int ) ch->pcdata->learned[skill];
}

void set_skill_level( CHAR_DATA *ch, int sn, int lvl )
{
  if( !IS_NPC( ch ) )
    {
      ch->pcdata->learned[sn] = lvl;
    }
}

void modify_skill_level( CHAR_DATA *ch, int sn, int value )
{
  if( !IS_NPC( ch ) )
    {
      set_skill_level( ch, sn, character_skill_level( ch, sn ) + value );
    }
}

/*
 * Dummy function
 */
void skill_notfound( CHAR_DATA * ch, char *argument )
{
  send_to_char( "Huh?\r\n", ch );
  return;
}

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


extern const char *target_name;	/* from magic.c */

/*
 * 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, const char *command, char *argument )
{
  int sn = 0;
  int first = gsn_first_skill;
  int top = gsn_first_weapon - 1;
  struct timeval time_used;
  int mana = 0;
  SKILLTYPE *skill = NULL;

  /* bsearch for the skill */
  for( ;; )
  {
    sn = ( first + top ) >> 1;
    skill = skill_table[sn];

    if( LOWER( command[0] ) == LOWER( skill->name[0] )
	&& !str_prefix( command, skill->name )
	&& ( skill->skill_fun
	  || skill->spell_fun != spell_null )
	&& ( IS_NPC( ch ) || ( character_skill_level( ch, sn ) > 0 ) ) )
      break;

    if( first >= top )
      return FALSE;

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

  if( !check_pos( ch, skill->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...\r\n",
	ch );
    act( AT_GREY, "$n looks around.", ch, NULL, NULL, TO_ROOM );
    return TRUE;
  }

  /* check if mana is required */
  if( skill->min_mana )
  {
    mana = IS_NPC( ch ) ? 0 : skill->min_mana;

    if( !IS_NPC( ch ) && ch->mana < mana )
    {
      send_to_char
	( "You need to rest before using the Force any more.\r\n", ch );
      return TRUE;
    }
  }
  else
  {
    mana = 0;
  }

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

    target_name = "";

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

      case TAR_IGNORE:
	vo = NULL;

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

	break;

      case TAR_CHAR_OFFENSIVE:
	if( argument[0] == '\0' && ( victim = who_fighting( ch ) ) == NULL )
	{
	  ch_printf( ch, "%s who?\r\n",
	      capitalize( skill->name ) );
	  return TRUE;
	}
	else if( argument[0] != '\0'
	    && ( victim = get_char_room( ch, argument ) ) == NULL )
	{
	  send_to_char( "They aren't here.\r\n", ch );
	  return TRUE;
	}

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

	vo = ( void * ) victim;
	break;

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

	if( !victim )
	  victim = ch;

	vo = ( void * ) victim;
	break;

      case TAR_CHAR_SELF:
	vo = ( void * ) ch;
	break;

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

	vo = ( void * ) obj;
	break;
    }

    /* waitstate */
    WAIT_STATE( ch, skill->beats );

    /* check for failure */
    if( ( number_percent() + skill->difficulty * 5 )
	> ( IS_NPC( ch ) ? 75 : character_skill_level( ch, sn ) ) )
    {
      failed_casting( skill, ch, ( CHAR_DATA * ) vo, 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->spell_fun ) ( sn, ch->top_level, ch, vo );
    end_timer( &time_used );
    update_userec( &time_used, &skill->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->target == TAR_CHAR_OFFENSIVE
	&& victim != ch && !char_died( victim ) )
    {
      CHAR_DATA *vch = NULL;
      CHAR_DATA *vch_next = NULL;

      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->last_cmd = skill->skill_fun;
  start_timer( &time_used );
  ( *skill->skill_fun ) ( ch, argument );
  end_timer( &time_used );
  update_userec( &time_used, &skill->userec );

  return TRUE;
}

/*
 * 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 = 0;
  SKILLTYPE *skill = NULL;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "Slookup what?\r\n", 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\r\n",
	  sn, skill_table[sn]->slot, skill_table[sn]->name,
	  spell_damage[SPELL_DAMAGE( skill_table[sn] )] );
  }
  else
  {
    SMAUG_AFF *aff = NULL;
    int cnt = 0;

    if( is_number( arg ) )
    {
      sn = atoi( arg );

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

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

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

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

    if( skill->flags )
    {
      int x = 0;

      ch_printf( ch,
	  "Damtype: %s  Acttype: %s   Classtype: %s   Powertype: %s\r\n",
	  spell_damage[SPELL_DAMAGE( skill )],
	  spell_action[SPELL_ACTION( skill )],
	  spell_class[SPELL_CLASS( skill )],
	  spell_power[SPELL_POWER( skill )] );
      strcpy( buf, "Flags:" );

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

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

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

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

    ch_printf( ch,
	"Type: %s  Target: %s  Minpos: %d  Mana: %d  Beats: %d\r\n",
	skill_tname[skill->type],
	target_type[URANGE
	( TAR_IGNORE, skill->target, TAR_OBJ_INV )],
	skill->minimum_position, skill->min_mana, skill->beats );
    ch_printf( ch, "Flags: %d  Code: %s\r\n", skill->flags,
	skill->skill_fun || skill->spell_fun ? skill->fun_name : "(none set)" );
    ch_printf( ch, "Dammsg: %s\r\nWearoff: %s\n", skill->noun_damage,
	skill->msg_off ? skill->msg_off : "(none set)" );
    if( skill->dice && skill->dice[0] != '\0' )
      ch_printf( ch, "Dice: %s\r\n", skill->dice );

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

    if( skill->participants )
      ch_printf( ch, "Participants: %d\r\n", ( 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( "\r\n", 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 )
	  strcat( buf, "' and" );
	else
	  strcat( buf, "'" );
      }

      if( aff->bitvector )
      {
	int x = 0;

	strcat( buf, " applies" );

	for( x = 0; x < 32; x++ )
	{
	  if( IS_SET( aff->bitvector, 1 << x ) )
	  {
	    strcat( buf, " " );
	    strcat( buf, a_flags[x] );
	  }
	}
      }

      if( aff->duration[0] != '\0' && 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, "\r\n" );
      send_to_char( buf, ch );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  if( arg1[0] == '\0' || arg2[0] == '\0' || argument[0] == '\0' )
  {
    send_to_char( "Syntax: sset <victim> <skill> <value>\r\n", ch );
    send_to_char( "or:     sset <victim> all     <value>\r\n", ch );
    if( IS_IMMORTAL( ch ) )
    {
      send_to_char( "or:     sset save skill table\r\n", ch );
      send_to_char( "or:     sset create skill 'new skill'\r\n", ch );
      send_to_char( "or:     sset <sn>     <field> <value>\r\n", ch );
      send_to_char( "\r\nField being one of:\r\n", ch );
      send_to_char
	( "  name code target minpos slot mana beats dammsg wearoff minlevel\r\n",
	  ch );
      send_to_char
	( "  type damtype acttype classtype powertype flag dice value difficulty affect\r\n",
	  ch );
      send_to_char
	( "  rmaffect level hit miss die imm (char/vict/room)\r\n",
	  ch );
      send_to_char( "  components\r\n", ch );
      send_to_char
	( "Affect having the fields: <location> <modfifier> [duration] [bitvector]\r\n",
	  ch );
      send_to_char
	( "(See AFFECTTYPES for location, and AFFECTED_BY for bitvector)\r\n",
	  ch );
    }
    send_to_char( "Skill being any skill or spell.\r\n", ch );
    return;
  }

  if( !str_cmp( arg1, "save" ) && !str_cmp( argument, "table" ) )
  {
    if( !str_cmp( arg2, "skill" ) )
    {
      send_to_char( "Saving skill table...\r\n", ch );
      save_skill_table();
      return;
    }
  }
  if( !str_cmp( arg1, "create" ) && !str_cmp( arg2, "skill" ) )
  {
    SKILLTYPE *skill = NULL;
    short type = SKILL_UNKNOWN;

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

    skill = create_skill();
    skill_table[top_sn++] = skill;
    skill->name = str_dup( argument );
    skill->fun_name = str_dup( "" );
    skill->noun_damage = str_dup( "" );
    skill->msg_off = str_dup( "" );
    skill->spell_fun = spell_smaug;
    skill->type = type;
    send_to_char( "Done.\r\n", ch );
    return;
  }

  if( arg1[0] == 'h' )
    sn = atoi( arg1 + 1 );
  else
    sn = atoi( arg1 );
  if( ( ( arg1[0] == 'h' && is_number( arg1 + 1 )
	  && ( sn = atoi( arg1 + 1 ) ) >= 0 ) || ( is_number( arg1 )
	  && ( sn =
	    atoi( arg1 ) ) >=
	  0 ) ) )
  {
    SKILLTYPE *skill = NULL;

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

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

      if( x == -1 )
	send_to_char( "Not a spell damage type.\r\n", ch );
      else
      {
	SET_SDAM( skill, x );
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "acttype" ) )
    {
      int x = get_saction( argument );

      if( x == -1 )
	send_to_char( "Not a spell action type.\r\n", ch );
      else
      {
	SET_SACT( skill, x );
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "classtype" ) )
    {
      int x = get_sclass( argument );

      if( x == -1 )
	send_to_char( "Not a spell class type.\r\n", ch );
      else
      {
	SET_SCLA( skill, x );
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "powertype" ) )
    {
      int x = get_spower( argument );

      if( x == -1 )
	send_to_char( "Not a spell power type.\r\n", ch );
      else
      {
	SET_SPOW( skill, x );
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "flag" ) )
    {
      int x = get_sflag( argument );

      if( x == -1 )
	send_to_char( "Not a spell flag.\r\n", ch );
      else
      {
	TOGGLE_BIT( skill->flags, 1 << ( x + 11 ) );
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "saves" ) )
    {
      int x = get_ssave( argument );

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

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

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

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

      if( x == -1 )
	send_to_char( "Not a valid target type.\r\n", ch );
      else
      {
	skill->target = x;
	send_to_char( "Ok.\r\n", ch );
      }
      return;
    }
    if( !str_cmp( arg2, "minpos" ) )
    {
      skill->minimum_position =
	URANGE( POS_DEAD, atoi( argument ), POS_DRAG );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "slot" ) )
    {
      skill->slot = URANGE( 0, atoi( argument ), 30000 );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "mana" ) )
    {
      skill->min_mana = URANGE( 0, atoi( argument ), 2000 );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "beats" ) )
    {
      skill->beats = URANGE( 0, atoi( argument ), 120 );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "value" ) )
    {
      skill->value = atoi( argument );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "type" ) )
    {
      skill->type = get_skill( argument );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    if( !str_cmp( arg2, "rmaffect" ) )
    {
      SMAUG_AFF *aff = skill->affects;
      SMAUG_AFF *aff_next = NULL;
      int num = atoi( argument );
      int cnt = 1;

      if( !aff )
      {
	send_to_char
	  ( "This spell has no special affects to remove.\r\n", ch );
	return;
      }
      if( num == 1 )
      {
	skill->affects = aff->next;
	DISPOSE( aff->duration );
	DISPOSE( aff->modifier );
	DISPOSE( aff );
	send_to_char( "Removed.\r\n", 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.\r\n", ch );
	  return;
	}
      }
      send_to_char( "Not found.\r\n", 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 = 0, bit = 0, tmpbit = 0;
      SMAUG_AFF *aff = NULL;

      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.\r\n",
	    ch );
	return;
      }

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

      CREATE( aff, SMAUG_AFF, 1 );
      if( !str_cmp( duration, "0" ) )
	duration[0] = '\0';
      if( !str_cmp( modifier, "0" ) )
	modifier[0] = '\0';
      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;
	sprintf( modifier, "%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, "name" ) )
    {
      if( *argument == '\0' )
      {
	ch_printf( ch, "Blank name not accepted.\r\n" );
	return;
      }

      if( get_skill( argument ) )
      {
	ch_printf( ch, "A skill with that name already exists.\r\n" );
	return;
      }

      DISPOSE( skill->name );
      skill->name = str_dup( argument );
      send_to_char( "Ok.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", 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.\r\n", ch );
      return;
    }
    do_sset( ch, STRLIT_EMPTY );
    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.\r\n", ch );
    return;
  }

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

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

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

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

  if( fAll )
    {
      for( sn = 0; sn < top_sn; sn++ )
	{
	  set_skill_level( victim, sn, value );
	}
    }
  else
    {
      set_skill_level( victim, sn, value );
    }
}

void learn_from_success( CHAR_DATA * ch, int sn )
{
  int adept = 100;

  if( IS_NPC( ch ) || character_skill_level( ch, sn ) == 0 )
    return;

  if( character_skill_level( ch, sn ) >= adept )
    return;

  if( character_skill_level( ch, sn ) < 100 )
  {
    int chance = character_skill_level( ch, sn ) / 4
      + 50 + ( 5 * skill_table[sn]->difficulty );
    int percent = number_percent();
    int learn = 0;

    if( percent > 95 )
      learn = 3;
    else if( percent > 90 )
      learn = 2;
    else if( percent > chance )
      learn = 1;
    set_skill_level( ch, sn, UMIN( adept, character_skill_level( ch, sn )
				   + learn ) );

    if( character_skill_level( ch, sn ) >= adept )	/* fully learned! */
    {
      set_char_color( AT_WHITE, ch );

      if( number_bits( 1 ) == 0 && sn != gsn_landscape )
      {
	send_to_char( "You feel somewhat brighter.\r\n", ch );
	ch->perm_int++;
	ch->perm_int = UMIN( ch->perm_int, 25 );
	set_skill_level( ch, sn, adept - 1 );
      }
      else
      {
	ch_printf( ch, "You are now an adept of %s!\r\n",
	    skill_table[sn]->name );
	ch->pcdata->adept_skills++;
      }
    }
  }
}

void learn_from_failure( CHAR_DATA * ch, int sn )
{
}

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

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

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

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

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

  percent = number_percent() - ( get_curr_lck( ch ) - 13 );

  if( IS_NPC( ch ) || percent < character_skill_level( ch, gsn_gouge ) )
  {
    dam = number_range( 1, character_skill_level( ch, gsn_gouge ) );
    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;
	af.duration =
	  3 + IS_NPC( ch ) ? ch->top_level : character_skill_level( ch,
	      gsn_gouge )
	  / 20;
	af.bitvector = 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 );
      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.
       */
    }
    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_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, *obj_next;
  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.\r\n", ch );
    return;
  }

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

  if( ms_find_obj( ch ) )
    return;

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

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

  if( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
  {
    set_char_color( AT_MAGIC, ch );
    send_to_char( "This isn't a good place to do that.\r\n", ch );
    return;
  }

  if( check_illegal_psteal( ch, victim ) )
  {
    send_to_char( "You can't steal from that player.\r\n", 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 )
    + times_killed( ch, victim ) * 7;

  if( victim->position == POS_FIGHTING
      || percent >
      ( IS_NPC( ch ) ? 90 : character_skill_level( ch, gsn_steal ) ) )
  {
    /*
     * Failure.
     */
    send_to_char( "Oops...\r\n", ch );
    act( AT_ACTION, "$n tried to steal from you!\r\n", ch, NULL, victim,
	TO_VICT );
    act( AT_ACTION, "$n tried to steal from $N.\r\n", 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;

      }
    }

    return;
  }

  if( IS_NPC( victim ) )
    add_kill( ch, victim );	/* makes it harder to steal from same char */

  if( !str_cmp( arg1, "credits" )
      || !str_cmp( arg1, "credit" ) || !str_cmp( arg1, "money" ) )
  {
    int amount;

    amount = ( int ) ( victim->gold * number_range( 1, 10 ) / 100 );
    if( amount <= 0 )
    {
      send_to_char( "You couldn't get any credits.\r\n", ch );
      learn_from_failure( ch, gsn_steal );
      return;
    }

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

  if( ( obj = get_obj_carry( victim, arg1 ) ) == NULL )
  {
    if( victim->position <= POS_SLEEPING )
    {
      if( ( obj = get_obj_wear( victim, arg1 ) ) != NULL )
      {
	if( ( obj_next = get_eq_char( victim, obj->wear_loc ) ) != obj )
	{
	  ch_printf( ch, "They are wearing %s on top of %s.\r\n",
	      obj_next->short_descr, obj->short_descr );
	  send_to_char( "You'll have to steal that first.\r\n", ch );
	  learn_from_failure( ch, gsn_steal );
	  return;
	}
	else
	  unequip_char( victim, obj );
      }
    }

    send_to_char( "You can't seem to find it.\r\n", 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 ) )
  {
    send_to_char( "You can't manage to pry it away.\r\n", 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.\r\n", 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.\r\n", ch );
    learn_from_failure( ch, gsn_steal );
    return;
  }

  send_to_char( "Ok.\r\n", ch );
  learn_from_success( ch, gsn_steal );
  separate_obj( obj );
  obj_from_char( obj );
  obj_to_char( obj, ch );
}

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.\r\n", ch );
    return;
  }

  one_argument( argument, arg );

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

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

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

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

  if( is_safe( ch, victim ) )
    return;

  /* Added stabbing weapon. -Narn */
  if( ( obj = get_eq_char( ch, WEAR_WIELD ) ) == NULL
      || ( obj->value[3] != WEAPON_VIBRO_BLADE ) )
  {
    send_to_char( "You need to wield a stabbing weapon.\r\n", ch );
    return;
  }

  if( victim->fighting )
  {
    send_to_char( "You can't backstab someone who is in combat.\r\n", ch );
    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 );

  WAIT_STATE( ch, skill_table[gsn_backstab]->beats );
  if( !IS_AWAKE( victim )
      || IS_NPC( ch ) || percent < character_skill_level( ch, gsn_backstab ) )
  {
    learn_from_success( ch, gsn_backstab );
    global_retcode = multi_hit( ch, victim, gsn_backstab );

  }
  else
  {
    learn_from_failure( ch, gsn_backstab );
    global_retcode = damage( ch, victim, 0, gsn_backstab );
  }
  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.\r\n", ch );
    return;
  }

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

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

  if( victim == ch )
  {
    send_to_char( "You try and rescue yourself, but fail miserably.\r\n",
	ch );
    return;
  }

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

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

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

  if( who_fighting( victim ) == ch )
  {
    send_to_char( "One would imagine THEY don't need your help.\r\n", ch );
    return;
  }

  ch->alignment = ch->alignment + 5;
  ch->alignment = URANGE( -1000, ch->alignment, 1000 );

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

  WAIT_STATE( ch, skill_table[gsn_rescue]->beats );
  if( !IS_NPC( ch ) && percent > character_skill_level( ch, gsn_rescue ) )
  {
    send_to_char( "You fail the rescue.\r\n", 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 );

  ch->alignment = ch->alignment + 50;
  ch->alignment = URANGE( -1000, ch->alignment, 1000 );

  learn_from_success( ch, gsn_rescue );
  stop_fighting( fch, FALSE );
  stop_fighting( victim, FALSE );
  if( ch->fighting )
    stop_fighting( ch, FALSE );

  /* check_killer( ch, fch ); */
  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.\r\n", ch );
    return;
  }

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

  WAIT_STATE( ch, skill_table[gsn_kick]->beats );
  if( IS_NPC( ch )
      || number_percent() < character_skill_level( ch, gsn_kick ) )
  {
    learn_from_success( ch, gsn_kick );
    global_retcode =
      damage( ch, victim,
	  number_range( 1,
	    IS_NPC( ch ) ? ch->top_level /
	    5 : character_skill_level( ch, gsn_kick ) / 5 ),
	  gsn_kick );
  }
  else
  {
    learn_from_failure( ch, gsn_kick );
    global_retcode = damage( ch, victim, 0, gsn_kick );
  }
  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 = NULL, *tmpobj = NULL;

  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_SKILL, "$n DISARMS you!", ch, NULL, victim, TO_VICT );
  act( AT_SKILL, "You disarm $N!", 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_room( obj, victim->in_room );
}


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.\r\n", ch );
    return;
  }

  if( !IS_NPC( ch ) && character_skill_level( ch, gsn_disarm ) <= 0 )
  {
    send_to_char( "You don't know how to disarm opponents.\r\n", ch );
    return;
  }

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

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

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

  WAIT_STATE( ch, skill_table[gsn_disarm]->beats );
  percent = number_percent()
    - ( get_curr_lck( ch ) - 15 ) + ( get_curr_lck( victim ) - 15 );
  if( !can_see_obj( ch, obj ) )
    percent += 10;
  if( IS_NPC( ch )
      || percent < character_skill_level( ch, gsn_disarm ) * 2 / 3 )
    disarm( ch, victim );
  else
  {
    send_to_char( "You failed.\r\n", 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 );
    REMOVE_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;
  SHIP_DATA *ship;

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

  one_argument( argument, arg );

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

  if( ms_find_obj( ch ) )
    return;

  if( ch->mount )
  {
    send_to_char( "You can't do that while mounted.\r\n", 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 ) )
    {
      act( AT_PLAIN, "$N is standing too close to the lock.",
	  ch, NULL, gch, TO_CHAR );
      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.\r\n", ch );
      return;
    }
    if( pexit->key < 0 )
    {
      send_to_char( "It can't be picked.\r\n", ch );
      return;
    }
    if( !IS_SET( pexit->exit_info, EX_LOCKED ) )
    {
      send_to_char( "It's already unlocked.\r\n", ch );
      return;
    }
    if( IS_SET( pexit->exit_info, EX_PICKPROOF ) )
    {
      send_to_char( "You failed.\r\n", ch );
      learn_from_failure( ch, gsn_pick_lock );
      return;
    }

    if( !IS_NPC( ch )
	&& number_percent() > character_skill_level( ch, gsn_pick_lock ) )
    {
      send_to_char( "You failed.\r\n", ch );
      learn_from_failure( ch, gsn_pick_lock );
      return;
    }

    REMOVE_BIT( pexit->exit_info, EX_LOCKED );
    send_to_char( "*Click*\r\n", ch );
    act( AT_ACTION, "$n picks the $d.", ch, NULL, pexit->keyword, TO_ROOM );
    learn_from_success( ch, gsn_pick_lock );
    /* 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 );
    }
    return;
  }

  if( ( obj = get_obj_here( ch, arg ) ) != NULL )
  {
    if( obj->item_type != ITEM_CONTAINER )
    {
      send_to_char( "You can't pick that.\r\n", ch );
      return;
    }
    if( !IS_SET( obj->value[1], CONT_CLOSED ) )
    {
      send_to_char( "It's not closed.\r\n", ch );
      return;
    }
    if( obj->value[2] < 0 )
    {
      send_to_char( "It can't be unlocked.\r\n", ch );
      return;
    }
    if( !IS_SET( obj->value[1], CONT_LOCKED ) )
    {
      send_to_char( "It's already unlocked.\r\n", ch );
      return;
    }
    if( IS_SET( obj->value[1], CONT_PICKPROOF ) )
    {
      send_to_char( "You failed.\r\n", ch );
      learn_from_failure( ch, gsn_pick_lock );
      return;
    }

    if( !IS_NPC( ch )
	&& number_percent() > character_skill_level( ch, gsn_pick_lock ) )
    {
      send_to_char( "You failed.\r\n", ch );
      learn_from_failure( ch, gsn_pick_lock );
      return;
    }

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

  if( ( ship = ship_in_room( ch->in_room, arg ) ) != NULL )
  {
    int chance;

    char buf[MAX_STRING_LENGTH];

    if( check_pilot( ch, ship ) )
    {
      send_to_char( "&RWhat would be the point of that!\r\n", ch );
      return;
    }

    if( ship->shipstate != SHIP_DOCKED && ship->shipstate != SHIP_DISABLED )
    {
      send_to_char( "&RThat ship has already started to launch", ch );
      return;
    }

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

    chance = URANGE( 0, character_skill_level( ch, gsn_pickshiplock ), 95 );

    if( IS_NPC( ch ) || !ch->pcdata || number_percent() > chance )
    {
      CHAR_DATA *wch;

      send_to_char( "You failed.\r\n", ch );
      sprintf( buf, "&R[ALARM] Someone is attempting to pick %s.",
	  ship->name );
      echo_to_area( ch->in_room->area, AT_RED, buf, 0 );
      for( wch = first_char; wch; wch = wch->next )
	if( !str_cmp( ship->owner, wch->name ) )
	  send_to_char( buf, ch );
      return;
    }

    if( !ship->hatchopen )
    {
      ship->hatchopen = TRUE;
      act( AT_PLAIN, "You pick the lock and open the hatch on $T.", ch,
	  NULL, ship->name, TO_CHAR );
      act( AT_PLAIN, "$n picks open the hatch on $T.", ch, NULL,
	  ship->name, TO_ROOM );
      echo_to_room( AT_YELLOW, ship->entrance,
	  "The hatch opens from the outside." );
      learn_from_success( ch, gsn_pickshiplock );
    }
    return;
  }

  ch_printf( ch, "You see no %s here.\r\n", 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.\r\n", ch );
    return;
  }

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

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

  if( IS_NPC( ch )
      || number_percent() < character_skill_level( ch, gsn_sneak ) )
  {
    af.type = gsn_sneak;
    af.duration =
      IS_NPC( ch ) ? ch->
      top_level : ( short ) ( character_skill_level( ch, gsn_sneak ) *
	  DUR_CONV );
    af.location = APPLY_NONE;
    af.modifier = 0;
    af.bitvector = 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.\r\n", ch );
    return;
  }

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

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

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

  if( IS_NPC( ch )
      || number_percent() < character_skill_level( ch, gsn_hide ) )
  {
    SET_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_invis );
  affect_strip( ch, gsn_mass_invis );
  affect_strip( ch, gsn_sneak );
  REMOVE_BIT( ch->affected_by, AFF_HIDE );
  REMOVE_BIT( ch->affected_by, AFF_INVISIBLE );
  REMOVE_BIT( ch->affected_by, AFF_SNEAK );
  send_to_char( "Ok.\r\n", ch );
}

void do_recall( CHAR_DATA * ch, char *argument )
{
  ROOM_INDEX_DATA *location = get_room_index( wherehome( ch ) );
  CHAR_DATA *opponent;

  if( !IS_IMMORTAL( ch ) )
  {
    return;
  }

  if( !location )
  {
    send_to_char( "You are completely lost.\r\n", ch );
    return;
  }

  if( ch->in_room == location )
    return;

  if( IS_SET( ch->affected_by, AFF_CURSE ) )
  {
    send_to_char( "You are cursed and cannot recall!\r\n", ch );
    return;
  }

  if( ( opponent = who_fighting( ch ) ) != NULL )
  {
    if( number_bits( 1 ) == 0
	|| ( !IS_NPC( opponent ) && number_bits( 3 ) > 1 ) )
    {
      WAIT_STATE( ch, 4 );
      ch_printf( ch, "You failed!\r\n" );
      return;
    }

    ch_printf( ch, "You recall from combat!\r\n" );
    stop_fighting( ch, TRUE );
  }

  act( AT_ACTION, "$n disappears in a swirl of the Force.", 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 a swirl of the Force.",
      ch, NULL, NULL, TO_ROOM );
  do_look( ch, STRLIT_AUTO );
}


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

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

  one_argument( argument, arg );
  if( arg[0] == '\0' )
  {
    send_to_char( "Aid whom?\r\n", ch );
    return;
  }

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

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

  if( victim == ch )
  {
    send_to_char( "Aid yourself?\r\n", ch );
    return;
  }

  if( victim->position > POS_STUNNED )
  {
    act( AT_PLAIN, "$N doesn't need your help.", ch, NULL, victim,
	TO_CHAR );
    return;
  }

  if( victim->hit <= -400 )
  {
    act( AT_PLAIN, "$N's condition is beyond your aiding ability.", ch,
	NULL, victim, TO_CHAR );
    return;
  }

  ch->alignment = ch->alignment + 20;
  ch->alignment = URANGE( -1000, ch->alignment, 1000 );

  percent = number_percent() - ( get_curr_lck( ch ) - 13 );
  WAIT_STATE( ch, skill_table[gsn_aid]->beats );
  if( !IS_NPC( ch ) && percent > character_skill_level( ch, gsn_aid ) )
  {
    send_to_char( "You fail.\r\n", ch );
    learn_from_failure( ch, gsn_aid );
    return;
  }

  ch->alignment = ch->alignment + 20;
  ch->alignment = URANGE( -1000, ch->alignment, 1000 );

  act( AT_SKILL, "You aid $N!", ch, NULL, victim, TO_CHAR );
  act( AT_SKILL, "$n aids $N!", ch, NULL, victim, TO_NOTVICT );
  learn_from_success( ch, gsn_aid );
  if( victim->hit < 1 )
    victim->hit = 1;

  update_pos( victim );
  act( AT_SKILL, "$n aids you!", ch, NULL, victim, TO_VICT );
  return;
}


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

  if( !IS_NPC( ch ) && character_skill_level( ch, gsn_mount ) <= 0 )
  {
    send_to_char( "I don't think that would be a good idea...\r\n", ch );
    return;
  }

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

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

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

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

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

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

  WAIT_STATE( ch, skill_table[gsn_mount]->beats );
  if( IS_NPC( ch )
      || number_percent() < character_skill_level( ch, gsn_mount ) )
  {
    SET_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.\r\n", ch );
    return;
  }

  WAIT_STATE( ch, skill_table[gsn_mount]->beats );
  if( IS_NPC( ch )
      || number_percent() < character_skill_level( ch, 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 );
    REMOVE_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 );
    REMOVE_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;
  OBJ_DATA *wield;

  if( !IS_AWAKE( victim ) )
    return FALSE;

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

  if( IS_NPC( victim ) )
  {
    /* Tuan was here.  :)   *** so was Durga :p *** */
    chances = UMIN( 60, victim->top_level );
  }
  else
  {
    if( ( wield = get_eq_char( victim, WEAR_WIELD ) ) == NULL ||
	( wield->value[3] != WEAPON_LIGHTSABER ) )
    {
      if( ( wield = get_eq_char( victim, WEAR_DUAL_WIELD ) ) == NULL ||
	  ( wield->value[3] != WEAPON_LIGHTSABER ) )
	return FALSE;
    }
    chances = character_skill_level( victim, gsn_parry );
  }

  chances = URANGE( 10, chances, 90 );

  if( number_range( 1, 100 ) > chances )
  {
    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( !IS_AWAKE( victim ) )
    return FALSE;

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

  if( IS_NPC( victim ) )
    chances = UMIN( 60, victim->top_level );
  else
    chances = character_skill_level( victim, gsn_dodge ) / 2;

  if( number_range( 1, 100 ) > chances )
  {
    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;
}

void do_poison_weapon( CHAR_DATA * ch, char *argument )
{
  OBJ_DATA *obj;
  OBJ_DATA *pobj;
  OBJ_DATA *wobj;
  char arg[MAX_INPUT_LENGTH];
  int percent;

  if( !IS_NPC( ch ) && character_skill_level( ch, gsn_poison_weapon ) <= 0 )
  {
    send_to_char( "What do you think you are, a thief?\r\n", ch );
    return;
  }

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "What are you trying to poison?\r\n", ch );
    return;
  }
  if( ch->fighting )
  {
    send_to_char( "While you're fighting?  Nice try.\r\n", ch );
    return;
  }
  if( ms_find_obj( ch ) )
    return;

  if( !( obj = get_obj_carry( ch, arg ) ) )
  {
    send_to_char( "You do not have that weapon.\r\n", ch );
    return;
  }
  if( obj->item_type != ITEM_WEAPON )
  {
    send_to_char( "That item is not a weapon.\r\n", ch );
    return;
  }
  if( IS_OBJ_STAT( obj, ITEM_POISONED ) )
  {
    send_to_char( "That weapon is already poisoned.\r\n", ch );
    return;
  }
  /* Now we have a valid weapon...check to see if we have the powder. */
  for( pobj = ch->first_carrying; pobj; pobj = pobj->next_content )
  {
    if( pobj->pIndexData->vnum == OBJ_VNUM_BLACK_POWDER )
      break;
  }
  if( !pobj )
  {
    send_to_char( "You do not have the black poison powder.\r\n", ch );
    return;
  }
  /* Okay, we have the powder...do we have water? */
  for( wobj = ch->first_carrying; wobj; wobj = wobj->next_content )
  {
    if( wobj->item_type == ITEM_DRINK_CON
	&& wobj->value[1] > 0 && wobj->value[2] == 0 )
      break;
  }
  if( !wobj )
  {
    send_to_char( "You have no water to mix with the powder.\r\n", ch );
    return;
  }
  /* And does the thief have steady enough hands? */
  if( !IS_NPC( ch ) && ( ch->pcdata->condition[COND_DRUNK] > 0 ) )
  {
    send_to_char
      ( "Your hands aren't steady enough to properly mix the poison.\r\n",
	ch );
    return;
  }
  WAIT_STATE( ch, skill_table[gsn_poison_weapon]->beats );

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

  /* Check the skill percentage */
  separate_obj( pobj );
  separate_obj( wobj );
  if( !IS_NPC( ch )
      && percent > character_skill_level( ch, gsn_poison_weapon ) )
  {
    set_char_color( AT_RED, ch );
    send_to_char( "You failed and spill some on yourself.  Ouch!\r\n", ch );
    set_char_color( AT_GREY, ch );
    damage( ch, ch, character_skill_level( ch, gsn_poison_weapon ),
	gsn_poison_weapon );
    act( AT_RED, "$n spills the poison all over!", ch, NULL, NULL,
	TO_ROOM );
    extract_obj( pobj );
    extract_obj( wobj );
    learn_from_failure( ch, gsn_poison_weapon );
    return;
  }
  separate_obj( obj );
  act( AT_RED, "You mix $p in $P, creating a deadly poison!", ch, pobj, wobj,
      TO_CHAR );
  act( AT_RED, "$n mixes $p in $P, creating a deadly poison!", ch, pobj, wobj,
      TO_ROOM );
  act( AT_GREEN, "You pour the poison over $p, which glistens wickedly!", ch,
      obj, NULL, TO_CHAR );
  act( AT_GREEN, "$n pours the poison over $p, which glistens wickedly!", ch,
      obj, NULL, TO_ROOM );
  SET_BIT( obj->extra_flags, ITEM_POISONED );
  obj->cost *= character_skill_level( ch, gsn_poison_weapon ) / 2;
  /* Set an object timer.  Don't want proliferation of poisoned weapons */
  obj->timer = 10 + character_skill_level( ch, gsn_poison_weapon );

  if( IS_OBJ_STAT( obj, ITEM_BLESS ) )
    obj->timer *= 2;

  if( IS_OBJ_STAT( obj, ITEM_MAGIC ) )
    obj->timer *= 2;

  /* WHAT?  All of that, just for that one bit?  How lame. ;) */
  act( AT_BLUE, "The remainder of the poison eats through $p.", ch, wobj,
      NULL, TO_CHAR );
  act( AT_BLUE, "The remainder of the poison eats through $p.", ch, wobj,
      NULL, TO_ROOM );
  extract_obj( pobj );
  extract_obj( wobj );
  learn_from_success( ch, gsn_poison_weapon );
  return;
}


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

  if( !IS_AWAKE( victim ) )
    return FALSE;

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

  if( IS_NPC( victim ) )
    chance = UMIN( 60, 2 * victim->top_level );
  else
    chance = character_skill_level( victim, gsn_grip ) / 2;

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

  if( number_percent() >= chance + victim->top_level - ch->top_level )
  {
    learn_from_failure( victim, gsn_grip );
    return FALSE;
  }

  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.\r\n", ch );
    return;
  }

  one_argument( argument, arg );

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

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

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

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

  if( is_safe( ch, victim ) )
    return;

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

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

  if( !victim->fighting )
  {
    send_to_char
      ( "You can't circle around a person who is not fighting.\r\n", 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 );

  WAIT_STATE( ch, skill_table[gsn_circle]->beats );
  if( percent < character_skill_level( ch, gsn_circle ) )
  {
    learn_from_success( ch, gsn_circle );
    global_retcode = multi_hit( ch, victim, gsn_circle );

  }
  else
  {
    learn_from_failure( ch, gsn_circle );
    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!\r\n", ch );
    return;
  }

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

  percent = IS_NPC( ch ) ? 80 : character_skill_level( ch, gsn_berserk );
  WAIT_STATE( ch, skill_table[gsn_berserk]->beats );
  if( !luck_check( ch, percent ) )
  {
    send_to_char( "You couldn't build up enough rage.\r\n", 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->top_level / 5, ch->top_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 = AFF_BERSERK;
  affect_to_char( ch, &af );
  send_to_char( "You start to lose control..\r\n", ch );
  learn_from_success( ch, gsn_berserk );
  return;
}

/* External from fight.c */
ch_ret one_hit( CHAR_DATA * ch, CHAR_DATA * victim, int dt );
void do_hitall( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *vch;
  CHAR_DATA *vch_next;
  short nvict = 0;
  short nhit = 0;
  short percent;

  if( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
  {
    send_to_char( "You cannot do that here.\r\n", ch );
    return;
  }

  if( !ch->in_room->first_person )
  {
    send_to_char( "There's no one here!\r\n", ch );
    return;
  }
  percent = IS_NPC( ch ) ? 80 : character_skill_level( ch, gsn_hitall );
  for( vch = ch->in_room->first_person; vch; vch = vch_next )
  {
    vch_next = vch->next_in_room;
    if( is_same_group( ch, vch ) || !is_legal_kill( ch, vch ) ||
	!can_see( ch, vch ) || is_safe( ch, vch ) )
      continue;
    if( ++nvict > character_skill_level( ch, gsn_hitall ) / 5 )
      break;
    if( luck_check( ch, percent ) )
    {
      nhit++;
      global_retcode = one_hit( ch, vch, TYPE_UNDEFINED );
    }
    else
      global_retcode = damage( ch, vch, 0, TYPE_UNDEFINED );
    /* Fireshield, etc. could kill ch too.. :>.. -- Altrag */
    if( global_retcode == rCHAR_DIED || global_retcode == rBOTH_DIED
	|| char_died( ch ) )
      return;
  }
  if( !nvict )
  {
    send_to_char( "There's no one here!\r\n", ch );
    return;
  }
  ch->move = UMAX( 0, ch->move - nvict * 3 + nhit );
  if( nhit )
    learn_from_success( ch, gsn_hitall );
  else
    learn_from_failure( ch, gsn_hitall );
  return;
}



bool check_illegal_psteal( CHAR_DATA * ch, CHAR_DATA * victim )
{
  return FALSE;
}

void do_scan( CHAR_DATA * ch, char *argument )
{
  ROOM_INDEX_DATA *was_in_room;
  ROOM_INDEX_DATA *to_room;
  EXIT_DATA *pexit;
  short dir = -1;
  short dist;
  short max_dist = 5;

  if( IS_AFFECTED( ch, AFF_BLIND )
      && ( !IS_AFFECTED( ch, AFF_TRUESIGHT ) ||
	( !IS_NPC( ch ) && !IS_SET( ch->act, PLR_HOLYLIGHT ) ) ) )
  {
    send_to_char( "Everything looks the same when you're blind...\r\n",
	ch );
    return;
  }

  if( argument[0] == '\0' )
    return;

  if( ( dir = get_door( argument ) ) == -1 )
    return;

  was_in_room = ch->in_room;

  if( ( pexit = get_exit( ch->in_room, dir ) ) == NULL )
  {
    act( AT_GREY, "You can't see any further $t.", ch, dir_name[dir], NULL,
	TO_CHAR );
    return;
  }

  if( ch->top_level < 50 )
    max_dist--;
  if( ch->top_level < 20 )
    max_dist--;

  for( dist = 1; dist <= max_dist; )
  {
    if( IS_SET( pexit->exit_info, EX_CLOSED ) )
    {
      if( IS_SET( pexit->exit_info, EX_SECRET ) )
	act( AT_GREY, "Your view $t is blocked by a wall.", ch,
	    dir_name[dir], NULL, TO_CHAR );
      else
	act( AT_GREY, "Your view $t is blocked by a door.", ch,
	    dir_name[dir], NULL, TO_CHAR );
      break;
    }

    to_room = pexit->to_room;

    if( room_is_private( ch, to_room ) && !IS_IMMORTAL( ch ) )
    {
      act( AT_GREY, "Your view $t is blocked by a private room.", ch,
	  dir_name[dir], NULL, TO_CHAR );
      break;
    }
    char_from_room( ch );
    char_to_room( ch, to_room );
    set_char_color( AT_RMNAME, ch );
    send_to_char( ch->in_room->name, ch );
    send_to_char( "\r\n", ch );
    show_list_to_char( ch->in_room->first_content, ch, FALSE, FALSE );
    show_char_to_char( ch->in_room->first_person, ch );

    switch ( ch->in_room->sector_type )
    {
      default:
	dist++;
	break;
      case SECT_AIR:
	if( number_percent() < 80 )
	  dist++;
	break;
      case SECT_INSIDE:
      case SECT_FIELD:
      case SECT_UNDERGROUND:
	dist++;
	break;
      case SECT_FOREST:
      case SECT_CITY:
      case SECT_DESERT:
      case SECT_HILLS:
	dist += 2;
	break;
      case SECT_WATER_SWIM:
      case SECT_WATER_NOSWIM:
	dist += 3;
	break;
      case SECT_MOUNTAIN:
      case SECT_UNDERWATER:
      case SECT_OCEANFLOOR:
	dist += 4;
	break;
    }

    if( dist >= max_dist )
    {
      act( AT_GREY, "Your vision blurs with distance and you see no "
	  "farther $t.", ch, dir_name[dir], NULL, TO_CHAR );
      break;
    }
    if( ( pexit = get_exit( ch->in_room, dir ) ) == NULL )
    {
      act( AT_GREY, "Your view $t is blocked by a wall.", ch,
	  dir_name[dir], NULL, TO_CHAR );
      break;
    }
  }

  char_from_room( ch );
  char_to_room( ch, was_in_room );

  return;
}

void do_slice( CHAR_DATA * ch, char *argument )
{
}

SKILLTYPE *create_skill( void )
{
  SKILLTYPE *skill = NULL;
  CREATE( skill, SKILLTYPE, 1 );
  return skill;
}