/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

thief.c					Thief skills and commands which
					which are 'usually' unique to
					thieves.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "handler.h"
#include "db.h"
#include "magic.h"
#include "mudlimits.h"
#include "fight.h"
#include "affect.h"
#include "lists.h"
#include "global.h"
#include "darkenelf.h"
#include "thief.h"

/* extern variables */
extern int rev_dir[];
extern char *comm_dirs[];
extern char *trap_types[];
extern char *trap_bits[];
extern char *dirs[];
extern struct str_app_type str_app[];
extern struct dex_skill_type dex_app_skill[];
extern int find_door(chdata *ch, char *type, char *door);

// support multiple traps to a buffer, short style
void traps_to_buf(chdata *ch, char *buf, trap *trp)
{
  trap *t = trp;

  for (*buf = '\0'; t ; t=t->next)
    if (IS_IMMORTAL(ch) || TRAPFLAGGED(t, TRF_REVEALED)) 
      sprintf(buf+strlen(buf), "(%%1%s%%0) ", trap_types[t->type]);

  return;
}

// trap support/code 4/10/98 -jtrhone
void trap_to_room(trap *t, rmdata *r)
{
  t->next = TRAPS(r);
  TRAPS(r) = t;
}

trap *trap_from_room(trap *t, rmdata *r)
{
  trap *temp;
  REMOVE_FROM_LIST(t, TRAPS(r), next);
  FREENULL(t);
  return t;
}

void trap_to_obj(trap *t, obdata *o)
{
  t->next = TRAPS(o);
  TRAPS(o) = t;
}

trap *trap_from_obj(trap *t, obdata *o)
{
  trap *temp;
  REMOVE_FROM_LIST(t, TRAPS(o), next);
  FREENULL(t);
  return t;
}

void trap_to_dir(trap *t, rmdirdata *d)
{
  t->next = TRAPS(d);
  TRAPS(d) = t;
}

trap *trap_from_dir(trap *t, rmdirdata *d)
{
  trap *temp;
  REMOVE_FROM_LIST(t, TRAPS(d), next);
  FREENULL(t);
  return t;
}

// actually detonate/release/pop a trap
int triptrap(trap *t, chdata *ch, BOOL area)
{
  int dam;
  chdata *v, *next_v;
  AffType af;

  sprintf(buf, "%%1AHHH!%%0  You tripped a %%B%%1%s%%0!\n\r", trap_types[TRAPTYPE(t)]);
  S2C();
  sprintf(buf, "%%B%%1$n tripped a %s!!%%0", trap_types[TRAPTYPE(t)]);
  act(buf, TRUE, ch, 0, 0, TO_ROOM);

  if (IS_IMMORTAL(ch))
    return CHAR_OK;

  // else, time to find the damage...
  switch (TRAPTYPE(t)) {
    case TRT_SNARE:
      dam = dice(t->level/5, 4);
      if (!area)
      {
        if (GET_DEX(ch) >= 18)
          dam /= 2;

        GET_HIT(ch) -= dam;
        update_pos(ch);
        if (GET_POS(ch) == POS_DEAD)
        {
          die(ch, FALSE);
          return CHAR_DIED;
        } 
      }
      else
      {
        int ch_died = FALSE;
        for (v = world[InRoom(ch)].people; v; v=next_v)
        {
          next_v = v->next_in_room;
          if (GET_DEX(v) >= 18)
            GET_HIT(v) -= dam/2;
          else
            GET_HIT(v) -= dam;
          update_pos(v);
          if (GET_POS(v) == POS_DEAD)
          {
            if (v == ch)
              ch_died = TRUE;
            die(v, FALSE);
          }
        }
        if (ch_died)
          return CHAR_DIED;
        else
          return CHAR_OK;
      }
      break;

    case TRT_DART:
      dam = t->level/5;
      if (!area)
      {
        if (GET_DEX(ch) >= 18)
          dam /= 2;

        GET_HIT(ch) -= dam;
        update_pos(ch);
        if (GET_POS(ch) == POS_DEAD)
        {
          die(ch, FALSE);
          return CHAR_DIED;
        } 
      }
      else
      {
        int ch_died = FALSE;
        for (v = world[InRoom(ch)].people; v; v=next_v)
        {
          next_v = v->next_in_room;
          if (GET_DEX(v) >= 18)
            GET_HIT(v) -= dam/2;
          else
            GET_HIT(v) -= dam;
          update_pos(v);
          if (GET_POS(v) == POS_DEAD)
          {
            if (v == ch)
              ch_died = TRUE;
            die(v, FALSE);
          }
        }
        if (ch_died)
          return CHAR_DIED;
        else
          return CHAR_OK;
      }
      break;

    case TRT_FIRE:
      dam = t->level/3;
      if (!area)
      {
        if (saves_spell(ch, SPELL_FIREBALL))
          dam /= 2;

        GET_HIT(ch) -= dam;
        update_pos(ch);
        if (GET_POS(ch) == POS_DEAD)
        {
          die(ch, FALSE);
          return CHAR_DIED;
        } 
      }
      else
      {
        int ch_died = FALSE;
        for (v = world[InRoom(ch)].people; v; v=next_v)
        {
          next_v = v->next_in_room;
          if (saves_spell(v, SPELL_FIREBALL))
            GET_HIT(v) -= dam/2;
          else
            GET_HIT(v) -= dam;
          update_pos(v);
          if (GET_POS(v) == POS_DEAD)
          {
            if (v == ch)
              ch_died = TRUE;
            die(v, FALSE);
          }
        }
        if (ch_died)
          return CHAR_DIED;
        else
          return CHAR_OK;
      }
      break;

    case TRT_LIGHTNING:
      dam = t->level;
      if (!area)
      {
        if (saves_spell(ch, SPELL_LIGHTNING_BOLT))
          dam /= 2;

        GET_HIT(ch) -= dam;
        update_pos(ch);
        if (GET_POS(ch) == POS_DEAD)
        {
          die(ch, FALSE);
          return CHAR_DIED;
        } 
      }
      else
      {
        int ch_died = FALSE;
        for (v = world[InRoom(ch)].people; v; v=next_v)
        {
          next_v = v->next_in_room;
          if (saves_spell(v, SPELL_LIGHTNING_BOLT))
            GET_HIT(v) -= dam/2;
          else
            GET_HIT(v) -= dam;
          update_pos(v);
          if (GET_POS(v) == POS_DEAD)
          {
            if (v == ch)
              ch_died = TRUE;
            die(v, FALSE);
          }
        }
        if (ch_died)
          return CHAR_DIED;
        else
          return CHAR_OK;
      }
      break;

    case TRT_POISON:
      dam = t->level/5;
  
      // create the poison affect...
      NewAffect(&af);
      af.type = SPELL_POISON;
      af.duration = t->level;
      af.modifier = -2;
      af.location = APPLY_STR;
      af.bitvector = AFF_POISON;

      if (!area)
      {
        if (GET_DEX(ch) >= 18)
          dam /= 2;

        GET_HIT(ch) -= dam;

        if (!affected_by_spell(ch, SKILL_CLEANSE) && !saves_spell(ch, SPELL_POISON))
        {
          affect_join(ch, &af, FALSE, FALSE);
          act("You feel very sick.", TRUE, ch, 0, 0, TO_CHAR);
          act("$n suddenly looks sick.", TRUE, ch, 0, 0, TO_ROOM);
        }

        update_pos(ch);
        if (GET_POS(ch) == POS_DEAD)
        {
          die(ch, FALSE);
          return CHAR_DIED;
        } 
      }
      else
      {
        int ch_died = FALSE;
        for (v = world[InRoom(ch)].people; v; v=next_v)
        {
          next_v = v->next_in_room;
          if (GET_DEX(v) >= 18)
            GET_HIT(v) -= dam/2;
          else
            GET_HIT(v) -= dam;

          if (!affected_by_spell(v, SKILL_CLEANSE) && !saves_spell(v, SPELL_POISON))
          {
            affect_join(v, &af, FALSE, FALSE);
            act("You feel very sick.", TRUE, v, 0, 0, TO_CHAR);
            act("$n suddenly looks sick.", TRUE, v, 0, 0, TO_ROOM);
          }

          update_pos(v);
          if (GET_POS(v) == POS_DEAD)
          {
            if (v == ch)
              ch_died = TRUE;
            die(v, FALSE);
          }
        }
        if (ch_died)
          return CHAR_DIED;
        else
          return CHAR_OK;
      }
      break;

    default:
      break;
  }

  return CHAR_OK;
}

// search in a direction for a room snare
void do_detect_snare(chdata *ch, char *arg)
{
  int dir, toroom;
  rmdata *r;

  // they are lookin at a direction
  dir = search_block(arg, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: detect snare <[direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir)) 
  {
    act("It would be difficult detect a snare in that direction.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  toroom = EXIT(ch, dir)->to_room;
  if (INVALID_ROOM(toroom))
  {
    act("Invalid toroom, notify immortal.", FALSE,ch,0,0,TO_CHAR);
    return;
  }

  r = &world[toroom];

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  // ok, less do it
  // if they fail the skill or simply miss the trap...
  if (GET_SKILL(ch, SKILL_DETECTSNARE) < number(1, 101) || GET_LEVEL(ch) < number(1, 101))
  {
    act("You are unable to detect any snares in that direction.",FALSE,ch,0,0,TO_CHAR);
   
    // if hard to detect, or rigged for detection, make another roll
    if (TRAPS(r) && TRAPFLAGGED(TRAPS(r), TRF_ANTIDETECT) && GET_LEVEL(ch) < 70)
      if (GET_SKILL(ch, SKILL_DETECTSNARE) < number(1, 101))
      {
        triptrap(TRAPS(r), ch, TRUE);
        TRAPS(r) = trap_from_room(TRAPS(r), r);
      }
    return;
  }

  if (!TRAPS(r))
  {
    act("You are unable to detect any snares in that direction.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You have detected a %s to the %s!", trap_types[TRAPTYPE(TRAPS(r))], dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_CHAR);
  
  FLAGTRAP(TRAPS(r), TRF_REVEALED);
}

// search a direction or object for a trap...
void do_detect_trap(chdata *ch, char *arg)
{
  int dir;
  obdata *ob;
  chdata *tch;

  generic_find(arg, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &tch, &ob);

  // if they are lookin at an object
  if (ob) 
  {
    if (ITEM_TYPE(ob) != ITEM_CONTAINER || IS_CORPSE(ob))
    {
      act("It would be difficult to trap $p.",FALSE,ch,ob,0,TO_CHAR);
      return;
    }

    act("You examine $p.",TRUE,ch,ob,0,TO_CHAR);
    act("$n examines $p.",TRUE,ch,ob,0,TO_ROOM);

    // if they fail the skill or simply miss the trap...
    if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, 101) ||
        GET_LEVEL(ch) < number(1, 101))
    {
      act("You are unable to detect any traps on $p.",FALSE,ch,ob,0,TO_CHAR);
   
      // if hard to detect, or rigged for detection, make another roll
      if (TRAPS(ob) && TRAPFLAGGED(TRAPS(ob), TRF_ANTIDETECT) && GET_LEVEL(ch) < 70)
        if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, 101))
        {
          triptrap(TRAPS(ob), ch, FALSE);
   	  TRAPS(ob) = trap_from_obj(TRAPS(ob), ob);
	}
      return;
    }

    if (!TRAPS(ob))
    {
      act("You are unable to detect any traps on $p.",FALSE,ch,ob,0,TO_CHAR);
      return;
    } 
    
    sprintf(buf, "You have detected a %s on $p!", trap_types[TRAPTYPE(TRAPS(ob))] );
    act(buf, FALSE, ch, ob, 0, TO_CHAR);
  
    FLAGTRAP(TRAPS(ob), TRF_REVEALED);
    return;
  }

  // else, they are lookin at a direction
  dir = search_block(arg, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: detect trap <[object] | [direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to hide a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,ob,0,TO_ROOM);

  // if they fail the skill or simply miss the trap...
  if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, 101) || GET_LEVEL(ch) < number(1, 101))
  {
    act("You are unable to detect any traps in that direction.",FALSE,ch,0,0,TO_CHAR);
   
    // if hard to detect, or rigged for detection, make another roll
    if (TRAPS(EXIT(ch, dir)) && 
        TRAPFLAGGED(TRAPS(EXIT(ch, dir)), TRF_ANTIDETECT) && GET_LEVEL(ch) < 70)
      if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, 101))
      {
        triptrap(TRAPS(EXIT(ch, dir)), ch, FALSE);
   	TRAPS(EXIT(ch, dir)) = trap_from_dir(TRAPS(EXIT(ch, dir)), EXIT(ch, dir));
      }
    return;
  }

  if (!TRAPS(EXIT(ch, dir)))
  {
    act("You are unable to detect any traps in that direction.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You have detected a %s to the %s!", 
          trap_types[TRAPTYPE(TRAPS(EXIT(ch, dir)))], dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_CHAR);
  
  FLAGTRAP(TRAPS(EXIT(ch, dir)), TRF_REVEALED);
}

// search in a direction for a room snare to remove 4/14/98 -jtrhone
void do_remove_snare(chdata *ch, char *arg)
{
  int dir, toroom, prob = 101;
  rmdata *r;

  // they are lookin at a direction
  dir = search_block(arg, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: remove snare <[direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir)) 
  {
    act("It would be difficult to remove a snare in that direction.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  toroom = EXIT(ch, dir)->to_room;
  if (INVALID_ROOM(toroom))
  {
    act("Invalid toroom, notify immortal.", FALSE,ch,0,0,TO_CHAR);
    return;
  }

  r = &world[toroom];

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  // ok, less do it
  if (!TRAPS(r) || !TRAPFLAGGED(TRAPS(r), TRF_REVEALED))
  {
    act("There are no visible snares in that direction.",FALSE,ch,0,0,TO_CHAR);
    return; 
  }

  // ok, the trap is there, and it is revealed... attempt removal

  // if its antiremove, it's twice as hard to remove
  if (TRAPFLAGGED(TRAPS(r), TRF_ANTIREMOVE) && GET_LEVEL(ch) < 65)
    prob *= 2;

  if (GET_SKILL(ch, SKILL_REMOVESNARE) < number(1, prob) || GET_LEVEL(ch) < number(1, 101))
  {
    triptrap(TRAPS(r), ch, TRUE);
    TRAPS(r) = trap_from_room(TRAPS(r), r);
    return;
  }

  sprintf(buf, "You have removed the %s to the %s!", trap_types[TRAPTYPE(TRAPS(r))], dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_CHAR);

  sprintf(buf, "$n has removed the %s to the %s!", trap_types[TRAPTYPE(TRAPS(r))], dirs[dir]);
  act(buf, TRUE, ch, 0, 0, TO_ROOM);
  
  TRAPS(r) = trap_from_room(TRAPS(r), r);
}

// search a direction or object for a trap...to remove  4/14/98 -jtrhone
void do_remove_trap(chdata *ch, char *arg)
{
  int dir, prob = 101;
  obdata *ob;
  chdata *tch;

  generic_find(arg, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &tch, &ob);

  // if they are lookin at an object
  if (ob) 
  {
    if (ITEM_TYPE(ob) != ITEM_CONTAINER || IS_CORPSE(ob))
    {
      act("It would be difficult to trap $p.",FALSE,ch,ob,0,TO_CHAR);
      return;
    }

    act("You examine $p.",TRUE,ch,ob,0,TO_CHAR);
    act("$n examines $p.",TRUE,ch,ob,0,TO_ROOM);

    if (!TRAPS(ob) || !TRAPFLAGGED(TRAPS(ob), TRF_REVEALED))
    {
      act("There are no visible traps on $p.",FALSE,ch,ob,0,TO_CHAR);
      return; 
    }

    // if its antiremove, it's twice as hard to remove
    if (TRAPFLAGGED(TRAPS(ob), TRF_ANTIREMOVE) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_REMOVETRAP) < number(1, prob) || GET_LEVEL(ch) < number(1, 101))
    {
      triptrap(TRAPS(ob), ch, FALSE);
      TRAPS(ob) = trap_from_obj(TRAPS(ob), ob);
      return;
    }

    sprintf(buf, "You have removed the %s on $p!", trap_types[TRAPTYPE(TRAPS(ob))] );
    act(buf, FALSE, ch, ob, 0, TO_CHAR);

    sprintf(buf, "$n has removed the %s on $p!", trap_types[TRAPTYPE(TRAPS(ob))] );
    act(buf, TRUE, ch, ob, 0, TO_ROOM);

    TRAPS(ob) = trap_from_obj(TRAPS(ob), ob);
    return;
  }

  // else, they are lookin at a direction
  dir = search_block(arg, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: remove trap <[object] | [direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to hide a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  // ok, less do it
  if (!TRAPS(EXIT(ch, dir)) || !TRAPFLAGGED(TRAPS(EXIT(ch, dir)), TRF_REVEALED))
  {
    act("There are no visible traps in that direction.",FALSE,ch,0,0,TO_CHAR);
    return; 
  }

  // ok, the trap is there, and it is revealed... attempt removal

  // if its antiremove, it's twice as hard to remove
  if (TRAPFLAGGED(TRAPS(EXIT(ch, dir)), TRF_ANTIREMOVE) && GET_LEVEL(ch) < 65)
    prob *= 2;

  if (GET_SKILL(ch, SKILL_REMOVETRAP) < number(1, prob) || GET_LEVEL(ch) < number(1, 101))
  {
    triptrap(TRAPS(EXIT(ch, dir)), ch, FALSE);
    TRAPS(EXIT(ch, dir)) = trap_from_dir(TRAPS(EXIT(ch, dir)), EXIT(ch, dir));
    return;
  }

  sprintf(buf, "You have removed the %s to the %s!", trap_types[TRAPTYPE(TRAPS(EXIT(ch, dir)))], dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_CHAR);

  sprintf(buf, "$n has removed the %s to the %s!", trap_types[TRAPTYPE(TRAPS(EXIT(ch, dir)))], dirs[dir]);
  act(buf, TRUE, ch, 0, 0, TO_ROOM);
  
  TRAPS(EXIT(ch, dir)) = trap_from_dir(TRAPS(EXIT(ch, dir)), EXIT(ch, dir));
}

// allow a thief to make an exit secret  4/26/98 -jtrhone
ACMD(do_obscure)
{
  char *argu = argument;
  rmdirdata *d;
  int dir;

  if (IS_NPC(ch))
    return;
 
  if (!GET_SKILL(ch, SKILL_OBSCURE))
  {
    act("You do not posess that ability.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Usage: obscure < direction >.\n\r",ch);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: obscure < direction >.\n\r",ch);
    return;
  }

  if (!(d = EXIT(ch, dir)) || EXIT_SECRET(d))
  {
    act("It would be fairly difficult to obscure anything there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  if (!EXIT_ISDOOR(d))
    act("It would be fairly difficult to obscure anything there.",FALSE,ch,0,0,TO_CHAR);
  else if (EXIT_FLAGGED(d, EX_NOOBSCURE))
    send_to_char("You fail to obscure it from view.\n\r", ch);
  else
  {
    // skill check :)
    if (GET_SKILL(ch, SKILL_OBSCURE) < number(1, 101))
    {
      act("You fail to obscure it from view.", FALSE, ch, 0,0,TO_CHAR);
      return;
    }

    FLAG_EXIT(d, EX_SECRET);
    if (d->keyword)
    {
      act("You obscure the $F from view.", FALSE, ch, 0, d->keyword, TO_CHAR);
      act("$n obscures the $F from view.", TRUE, ch, 0, d->keyword, TO_ROOM);
    }
    else
    {
      act("You obscure it from view.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n obscures something from view.", TRUE, ch, 0, 0, TO_ROOM);
    }
    // only obscure exits 1 WAY!  (do not obscure other side...)
  }
}

// allow thief to poison a wielded weapon  4/19/98 -jtrhone
ACMD(do_poisonblade)
{
  obdata *h, *w;
  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_POISONBLADE))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (!(h = EQ(ch, W_HOLD)) || !IS_PUREPOISON(h))
  {
    act("You need to be holding poison to do such.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  if (DRINKS_LEFT(h) <= 0)
  {
    act("You have no poison left.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  if (!(w = EQ(ch, W_WIELD)) || !IS_WEAPON(w))
  {
     send_to_char("You need to be wielding a slashing or piercing weapon.\n\r", ch);
     return;
  }

  if (w->value[3] != (TYPE_BACKSTAB     - TYPE_HIT) &&
      w->value[3] != (TYPE_NO_BS_PIERCE - TYPE_HIT) &&
      w->value[3] != (TYPE_SLASH        - TYPE_HIT)) {
     send_to_char("You can only apply poison to slashing and piercing weapons.\n\r", ch);
     return;
  }

  DRINKS_LEFT(h)--;

  if (GET_SKILL(ch, SKILL_POISONBLADE) < number(1, 101))
  {
    act("You fail to add poison to $p.", FALSE, ch, w, 0, TO_CHAR);
    return;
  }

  w->poisoned = TRUE;
  w->poison_duration++;

  act("You apply a bit of %2poison%0 to $p.", FALSE, ch, w, 0, TO_CHAR);
  act("$n applies a bit of %2poison%0 to $p.", TRUE, ch, w, 0, TO_ROOM);
}

// allow thief to jam an exit open or closed  4/19/98 -jtrhone
ACMD(do_jamlock)
{
  char *argu = argument;
  int dir, other_room;
  rmdirdata *d, *back;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_JAMLOCK))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    act("Jam what?", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: jam <[direction]>.\n\r",ch);  
    return;
  }

  if (!(d = EXIT(ch, dir)))
  {
    act("It would be fairly difficult to jam anything there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  if (!EXIT_ISDOOR(d))
    act("It would be fairly difficult to jam anything there.",FALSE,ch,0,0,TO_CHAR);
  else if (EXIT_JAMMED(d))
    send_to_char("It already seems to be jammed.\n\r", ch);
  else if (EXIT_FLAGGED(d, EX_NOJAM) || EXIT_FLAGGED(d, EX_PICKPROOF))
    send_to_char("You fail to jam it.\n\r", ch);
  else
  {
    // skill check :)
    if (GET_SKILL(ch, SKILL_JAMLOCK) < number(1, 101))
    {
      act("You fail to jam it.", FALSE, ch, 0,0,TO_CHAR);
      return;
    }

    FLAG_EXIT(d, EX_JAMMED);
    if (d->keyword)
    {
      act("You jam the $F.", FALSE, ch, 0, d->keyword, TO_CHAR);
      act("$n jams the $F.", TRUE, ch, 0, d->keyword, TO_ROOM);
    }
    else
    {
      act("You jam it.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n jams something.", TRUE, ch, 0, 0, TO_ROOM);
    }

    /* now for jamming the other side, too */
    if ((other_room = d->to_room) != NOWHERE)
      if ((back = DIR(other_room, rev_dir[dir])))
        if (back->to_room == ch->in_room)
          FLAG_EXIT(back, EX_JAMMED);
  }
}

// shadowwalk, substained hide  4/21/98 -jtrhone
ACMD(do_shadowwalk)
{
  AffType af;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_SHADOWWALK))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (FIGHTING(ch))
  {
    act("You cannot do so while fighting.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (IS_AFFECTED2(ch, AFF2_ILLUMINATE))
  {
    act("You cannot do so while illuminated.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (affected_by_spell(ch, SKILL_SHADOWWALK))
  {
    act("You are already walking with the shadows.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_SHADOWWALK) < number(1, 101))
  {
    act("You fail to walk with the shadows.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  act("You meld into the shadows.", FALSE, ch, 0,0,TO_CHAR);
  act("$n melds into the shadows.", TRUE, ch, 0,0,TO_ROOM);

  NewAffect(&af);
  af.type = SKILL_SHADOWWALK;
  af.duration = GET_LEVEL(ch);
  af.bitvector = AFF_HIDE;
  affect_to_char(ch, &af);
}

// tumble, combat affect 4/21/98 -jtrhone
ACMD(do_tumble)
{
  AffType af;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_TUMBLE))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (!FIGHTING(ch))
  {
    act("You must be fighting to tumble.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (IS_AFFECTED2(ch, AFF2_ILLUMINATE))
  {
    act("You cannot do so while illuminated.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (affected_by_spell(ch, SKILL_TUMBLE))
  {
    act("You are already tumbling.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_TUMBLE) < number(1, 101))
  {
    act("You fall on your face!", FALSE, ch, 0,0,TO_CHAR);
    WAIT_STATE(ch, PULSE_VIOLENCE * 3);
    return;
  }

  act("You begin to tumble.", FALSE, ch, 0,0,TO_CHAR);
  act("$n begins to tumble.", TRUE, ch, 0,0,TO_ROOM);

  NewAffect(&af);
  af.type = SKILL_TUMBLE;
  af.duration = GET_LEVEL(ch)/10;
  affect_to_char(ch, &af);
}

// combat skill blindstrike...4/18/98 -jtrhone
// add wait if failure  6/19/98 -jtrhone
ACMD(do_blindstrike)
{
  AffType af;
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_BLINDSTRIKE))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_BLINDSTRIKE) < number(1, 101))
  {
    act("You fail to blindstrike $N.", FALSE, ch, 0, vict, TO_CHAR);
    WAIT_STATE(ch, PULSE_VIOLENCE * 3);
    return;
  }

  if (IS_AFFECTED(vict, AFF_BLIND))
  {
    act("$N is already somewhat blind.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

  if (MOB_FLAGGED(vict, MOB_NO_BLIND) || saves_spell(vict, SPELL_BLINDNESS))
  {
    act("You fail to %B%1blindstrike%0 $N!", FALSE, ch, 0, vict, TO_CHAR);
    act("$n fails to %B%1blindstrikes%0 you!", TRUE, ch, 0, vict, TO_VICT);
    act("$n fails to %B%1blindstrikes%0 $N!", FALSE, ch, 0, vict, TO_NOTVICT);
    WAIT_STATE(ch, PULSE_VIOLENCE * 3);
    return;
  }

  act("You %B%1blindstrike%0 $N!", FALSE, ch, 0, vict, TO_CHAR);
  act("$n %B%1blindstrikes%0 you!", TRUE, ch, 0, vict, TO_VICT);
  act("$n %B%1blindstrikes%0 $N!", FALSE, ch, 0, vict, TO_NOTVICT);

  NewAffect(&af);
  af.type = SKILL_BLINDSTRIKE;
  af.duration = GET_LEVEL(ch) / 10;   // rounds, decremented per round (affect.c)
  af.location  = APPLY_HITROLL;
  af.modifier  = -(GET_LEVEL(ch)/3);  /* Make hitroll WAY worse */ 
  af.bitvector = AFF_BLIND;
  affect_to_char(vict, &af);

  WAIT_STATE(ch, PULSE_VIOLENCE * 2);
}

// combat skill lowstrike...4/19/98 -jtrhone
ACMD(do_lowstrike)
{
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_LOWSTRIKE))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_LOWSTRIKE) < number(1, 101) || (GET_DEX(vict)-13) > number(1,10))
  {
    act("You fail to lowstrike $N.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

  act("You prepare to lowstrike $N.", FALSE, ch, 0, vict, TO_CHAR);

  // the actually affect is taken care of in fight.c
  SET_BIT(CHAR_FLAGS(ch), CH_LOWSTRIKE);
  VWAIT(ch) = 2;
  WAIT_STATE(ch, PULSE_VIOLENCE * 2);
}

// combat skill hamstring...4/26/98 -jtrhone
ACMD(do_hamstring)
{
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_HAMSTRING))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_HAMSTRING) < number(1, 101) || (GET_DEX(vict)-13) > number(1,10))
  {
    act("You fail to hamstring $N.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

  if (CHAR_FLAGGED(vict, CH_HAMSTRUNG))
  {
    act("$N is already hamstrung.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

  act("You hamstring $N!", FALSE, ch, 0, vict, TO_CHAR);
  act("$n hamstrings you!", FALSE, ch, 0, vict, TO_VICT);
  act("$n hamstrings $N!", TRUE, ch, 0, vict, TO_NOTVICT);

  // the actually affect is taken care of in fight.c
  SET_BIT(CHAR_FLAGS(vict), CH_HAMSTRUNG);
  VWAIT(ch) = 2;
  WAIT_STATE(ch, PULSE_VIOLENCE * 2);
}

// combat skill sidestep...4/21/98 -jtrhone
ACMD(do_sidestep)
{
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_SIDESTEP))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_SIDESTEP) < number(1, 101) || (GET_DEX(vict)-13) > number(1,10))
  {
    act("You fail to sidestep past $N.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

  act("You prepare to sidestep past $N.", FALSE, ch, 0, vict, TO_CHAR);

  // the actually affect is taken care of in fight.c
  SET_BIT(CHAR_FLAGS(ch), CH_SIDESTEP);
  WAIT_STATE(ch, PULSE_VIOLENCE * 2);
}

// combat skill disembowel...4/22/98 -jtrhone
ACMD(do_disembowel)
{
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_DISEMBOWEL))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_DISEMBOWEL) < number(1, 101) || (GET_DEX(vict)-13) > number(1,10))
  {
    act("You fail to disembowel $N.", FALSE, ch, 0, vict, TO_CHAR);
    WAIT_STATE(ch, PULSE_VIOLENCE * 3);
    return;
  }

  act("You prepare to disembowel $N.", FALSE, ch, 0, vict, TO_CHAR);

  // the actually affect is taken care of in fight.c
  SET_BIT(CHAR_FLAGS(ch), CH_DISEMBOWEL);
  WAIT_STATE(ch, PULSE_VIOLENCE * 3);
}

// combat skill bladedance...4/21/98 -jtrhone
ACMD(do_bladedance)
{
  chdata *vict;

  if (IS_NPC(ch))
    return;

  if (!(vict = FIGHTING(ch)))
  {
    act("You need to be fighting to use this skill.", FALSE,ch,0,0,TO_CHAR);
    return;
  }  

  if (!GET_SKILL(ch, SKILL_BLADEDANCE))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_BLADEDANCE) < number(1, 101) || (GET_DEX(vict)-13) > number(1,10))
  {
    act("You fail to bladedance around $N.", FALSE, ch, 0, vict, TO_CHAR);
    WAIT_STATE(ch, PULSE_VIOLENCE * 3);
    return;
  }

  act("You prepare to bladedance around $N.", FALSE, ch, 0, vict, TO_CHAR);

  // the actually affect is taken care of in fight.c
  SET_BIT(CHAR_FLAGS(ch), CH_BLADEDANCE);
  WAIT_STATE(ch, PULSE_VIOLENCE * 3);
}

// thiefs can close doors without key with this skill 4/18/98 -jtrhone
ACMD(do_seal)
{
  char *argu = argument;
  int dir, other_room;
  rmdirdata *d, *back;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_SEALEXIT))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    act("Seal what?", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: seal <[direction]>.\n\r",ch);  
    return;
  }

  if (!(d = EXIT(ch, dir)))
  {
    act("It would be fairly difficult to seal off anything there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  if (!EXIT_ISDOOR(d))
    act("It would be fairly difficult to seal off anything there.",FALSE,ch,0,0,TO_CHAR);
  else if (EXIT_OPEN(d))
    send_to_char("You have to close it first.\n\r", ch);
  else if (d->key < 0)
    send_to_char("There does not seem to be any keyholes.\n\r", ch);
  else if (EXIT_JAMMED(d))
    send_to_char("It seems to be jammed.\n\r", ch);
  else if (EXIT_FLAGGED(d, EX_PICKPROOF))
    send_to_char("You fail to seal it.\n\r", ch);
  else if (EXIT_LOCKED(d))
    send_to_char("It's already locked!\n\r", ch);
  else
  {
    // skill check :)
    if (GET_SKILL(ch, SKILL_SEALEXIT) < number(1, 101))
    {
      act("You fail to seal it.", FALSE, ch, 0,0,TO_CHAR);
      return;
    }

    FLAG_EXIT(d, EX_LOCKED);
    if (d->keyword)
      act("$n seals the $F.", TRUE, ch, 0, d->keyword, TO_ROOM);
    else
      act("$n seals something.", TRUE, ch, 0, 0, TO_ROOM);
    send_to_char("*Click*\n\r", ch);

    /* now for locking the other side, too */
    if ((other_room = d->to_room) != NOWHERE)
      if ((back = DIR(other_room, rev_dir[dir])))
        if (back->to_room == ch->in_room)
          FLAG_EXIT(back, EX_LOCKED);
  }
}

// allow thief to poison food water from pure poison held  4/17/98 -jtrhone
ACMD(do_poison)
{
  char *argu = argument;
  obdata *ob;
  chdata *tch;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_TPOISON))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    act("Poison what?", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  generic_find(argu, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &tch, &ob);
  if (!ob) 
  {
    act("Poison what?", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (!IS_LIQCONT(ob) && ITEM_TYPE(ob) != ITEM_FOOD && ITEM_TYPE(ob) != ITEM_FOUNTAIN)
  {
    act("You cannot poison $p.", FALSE, ch, ob, 0, TO_CHAR);
    return;
  }

  if (!EQ(ch, W_HOLD) || !IS_PUREPOISON(EQ(ch, W_HOLD)))
  {
    act("You need to be holding poison to do such.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  if (DRINKS_LEFT(EQ(ch, W_HOLD)) <= 0)
  {
    act("You have no poison left.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  DRINKS_LEFT(EQ(ch, W_HOLD))--;

  if (GET_SKILL(ch, SKILL_TPOISON) < number(1, 101))
  {
    act("You fail to begin the brewing process.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  DRINK_POISONED(ob) = TRUE;
  act("You poison $p.", FALSE, ch, ob, 0, TO_CHAR);
  act("$n poisons $p.", TRUE, ch, ob, 0, TO_ROOM);
}

// allow thief to brew a special poison IF they have an empty container held
ACMD(do_brew)
{
  AffType af;
  obdata *o;

  if (IS_NPC(ch))
    return;

  if (!GET_SKILL(ch, SKILL_BREW))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_BREW) < number(1, 101))
  {
    act("You fail to begin the brewing process.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (!(o = EQ(ch, W_HOLD)))
  {
    act("You must be holding an empty liquid container.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (!IS_LIQCONT(o) || DRINKS_LEFT(o))
  {
    act("You must be holding an empty liquid container.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (affected_by_spell(ch, SKILL_BREW))
  {
    act("You are already brewing some poison.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  NewAffect(&af);
  af.type = SKILL_BREW;
  af.duration = GET_LEVEL(ch)/10;
  affect_to_char(ch, &af);

  act("You begin to brew some poison in $p.", FALSE, ch, o, 0, TO_CHAR);
  act("$n begins to brew some poison in $p.", TRUE, ch, o, 0, TO_ROOM);
}

// allow thief to snoop 1 room away, even thru doors... 4/17/98 -jtrhone
ACMD(do_eavesdrop)
{
  char *argu = argument;
  rmdirdata *d;
  int dir;
  
  if (IS_NPC(ch))
    return;

  if (!ch->desc)
    return;

  if (!GET_SKILL(ch, SKILL_EAVESDROP))
  {
    act("You do not posess that ability.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Usage: eavesdrop <[direction]>.\n\r",ch);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: eavesdrop <[direction]>.\n\r",ch);  
    return;
  }

  if (!(d = EXIT(ch, dir)))
  {
    act("It would be fairly difficult to hear anything through that.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  if (INVALID_ROOM(d->to_room))
  {
    act("Invalid toroom, notify immortal.", FALSE,ch,0,0,TO_CHAR);
    return;
  }

  // remove if already eavesdroppin...
  if (CHAR_FLAGGED(ch, CH_EAVESDROPPING) && !INVALID_ROOM(ch->desc->room_snooping))
  {
    remove_room_snooper(&world[ch->desc->room_snooping], ch->desc);
    REMOVE_BIT(CHAR_FLAGS(ch), CH_EAVESDROPPING);
  }

  if (GET_SKILL(ch, SKILL_EAVESDROP) < number(1,101))
  {
    act("You fail to hear anything.", FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  act("You begin to listen intently.", FALSE, ch, 0,0,TO_CHAR);
  act("$n begins to listen intently.", TRUE, ch, 0,0,TO_ROOM);
  SET_BIT(CHAR_FLAGS(ch), CH_EAVESDROPPING);
  add_room_snooper(&world[d->to_room], ch->desc);
}

// now remove traps or snares... 4/14/98 -jtrhone
ACMD(do_remove)
{
  char *argu = argument;
  char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

  if (IS_NPC(ch))
    return;

  if (INVALID_ROOM(InRoom(ch)))
    return;

  if (IS_NPC(ch))
    return;

  skip_spaces(&argu);
  half_chop(argu, arg1, arg2);
  if (!*arg1 || !*arg2)
  {
    send_to_char("Usage: remove <trap | snare> <[object | direction]>.\n\r",ch);  
    return;
  }

  if (is_abbrev(arg1, "trap"))
    do_remove_trap(ch, arg2);
  else
  if (is_abbrev(arg1, "snare"))
    do_remove_snare(ch, arg2);
  else
    send_to_char("Usage: remove <trap | snare> <[object | direction]>.\n\r",ch);  
}

// front end to detect trap/snare/etc  4/11/98 -jtrhone
ACMD(do_detect)
{
  char *argu = argument;
  char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

  if (IS_NPC(ch))
    return;

  skip_spaces(&argu);
  half_chop(argu, arg1, arg2);
  if (!*arg1 || !*arg2)
  {
    send_to_char("Usage: detect <trap | snare> <[object | direction]>.\n\r",ch);  
    return;
  }

  if (is_abbrev(arg1, "trap"))
    do_detect_trap(ch, arg2);
  else
  if (is_abbrev(arg1, "snare"))
    do_detect_snare(ch, arg2);
  else
    send_to_char("Usage: detect <trap | snare> <[object | direction]>.\n\r",ch);  
}

// place a trap on an exit that does level / 5 piercing damage to who trips it
// 4/14/98 -jtrhone
ACMD(do_darttrap)
{
  char *argu = argument;
  int dir;
  rmdirdata *d;
  BOOL antidetect, antiremove;
  trap *t;

  if (IS_NPC(ch))
    return; // for now

  if (!GET_SKILL(ch, SKILL_DARTTRAP))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }
  
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  // they are lookin at a direction
  skip_spaces(&argu);
  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: darttrap <[direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to place a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  d = EXIT(ch, dir);

  // hmm, before we place, what if there is a trap there?
  if (TRAPS(d))
  {
    int prob = 101;
    if (TRAPFLAGGED(TRAPS(d), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
    {
      triptrap(TRAPS(d), ch, FALSE);
      TRAPS(d) = trap_from_dir(TRAPS(d), d);
    }
    else
      act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
    return;
  } 

  if (GET_SKILL(ch, SKILL_DARTTRAP) < number(1, 101) || EXIT_FLAGGED(d, EX_NOTRAP))
  {
    act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }
  
  // else, place the trap...
  antiremove = (GET_TRAP_LEVEL(ch) >= 65);
  antidetect = (GET_TRAP_LEVEL(ch) >= 70);

  CREATE(t, trap, 1);
  t->ch    = 0;		// design decision to NOT assign this
  t->level = GET_TRAP_LEVEL(ch);
  t->assassin = IS_ASSASSIN(ch);
  t->timer = 0;
  
  t->type = TRT_DART;
  FLAGTRAP(t, TRF_ARMED);
  if (antiremove)
    FLAGTRAP(t, TRF_ANTIREMOVE);
  if (antidetect)
    FLAGTRAP(t, TRF_ANTIDETECT);

  trap_to_dir(t, d);

  act("You successfully place the dart trap.",FALSE,ch,0,0,TO_CHAR);
}

// exit or container trap for level / 3 pts of damage  
// 4/21/98 -jtrhone
ACMD(do_firetrap)
{
  char *argu = argument;
  int dir;
  rmdirdata *d;
  BOOL antidetect, antiremove;
  trap *t;
  chdata *tch;
  obdata *ob;

  if (IS_NPC(ch))
    return; // for now

  if (!GET_SKILL(ch, SKILL_FIRETRAP))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }

  // check for object first...
  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Firetrap what?\n\r",ch);
    return;
  }
  
  generic_find(argu, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &tch, &ob);

  // if they are lookin at an object
  if (ob) 
  {
    if (ITEM_TYPE(ob) != ITEM_CONTAINER || IS_CORPSE(ob))
    {
      act("It would be difficult to trap $p.",FALSE,ch,ob,0,TO_CHAR);
      return;
    }

    act("You examine $p.",FALSE,ch,ob,0,TO_CHAR);
    act("$n examines $p.",TRUE,ch,ob,0,TO_ROOM);

    // hmm, before we place, what if there is a trap there?
    if (TRAPS(ob))
    {
      int prob = 101;
      if (TRAPFLAGGED(TRAPS(ob), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
        prob *= 2;

      if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
      {
        triptrap(TRAPS(ob), ch, FALSE);
        TRAPS(ob) = trap_from_obj(TRAPS(ob), ob);
      }
      else
        act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
      return;
    } 

    if (GET_SKILL(ch, SKILL_FIRETRAP) < number(1, 101))
    {
      act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
      return;
    }
  
    // else, place the trap...
    antiremove = (GET_TRAP_LEVEL(ch) >= 65);
    antidetect = (GET_TRAP_LEVEL(ch) >= 70);

    CREATE(t, trap, 1);
    t->ch    = 0;		// design decision to NOT assign this
    t->level = GET_TRAP_LEVEL(ch);
    t->assassin = IS_ASSASSIN(ch);
    t->timer = 0;
  
    t->type = TRT_FIRE;
    FLAGTRAP(t, TRF_ARMED);
    if (antiremove)
      FLAGTRAP(t, TRF_ANTIREMOVE);
    if (antidetect)
      FLAGTRAP(t, TRF_ANTIDETECT);

    trap_to_obj(t, ob);

    act("You successfully place the fire trap on $p.",FALSE,ch,ob,0,TO_CHAR);
    return;
  }

  // else they mean a direction...
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: firetrap <object | [direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to place a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  d = EXIT(ch, dir);

  // hmm, before we place, what if there is a trap there?
  if (TRAPS(d))
  {
    int prob = 101;
    if (TRAPFLAGGED(TRAPS(d), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
    {
      triptrap(TRAPS(d), ch, FALSE);
      TRAPS(d) = trap_from_dir(TRAPS(d), d);
    }
    else
      act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
    return;
  } 

  if (GET_SKILL(ch, SKILL_FIRETRAP) < number(1, 101) || EXIT_FLAGGED(d, EX_NOTRAP))
  {
    act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }
  
  // else, place the trap...
  antiremove = (GET_TRAP_LEVEL(ch) >= 65);
  antidetect = (GET_TRAP_LEVEL(ch) >= 70);

  CREATE(t, trap, 1);
  t->ch    = 0;		// design decision to NOT assign this
  t->level = GET_TRAP_LEVEL(ch);
  t->assassin = IS_ASSASSIN(ch);
  t->timer = 0;
  
  t->type = TRT_FIRE;
  FLAGTRAP(t, TRF_ARMED);
  if (antiremove)
    FLAGTRAP(t, TRF_ANTIREMOVE);
  if (antidetect)
    FLAGTRAP(t, TRF_ANTIDETECT);

  trap_to_dir(t, d);

  act("You successfully place the fire trap.",FALSE,ch,0,0,TO_CHAR);
}

// exit or container trap for level pts of damage  
// 4/22/98 -jtrhone
ACMD(do_lightningtrap)
{
  char *argu = argument;
  int dir;
  rmdirdata *d;
  BOOL antidetect, antiremove;
  trap *t;
  chdata *tch;
  obdata *ob;

  if (IS_NPC(ch))
    return; // for now

  if (!GET_SKILL(ch, SKILL_LIGHTNINGTRAP))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }

  // check for object first...
  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Lightningtrap what?\n\r",ch);
    return;
  }
  
  generic_find(argu, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &tch, &ob);

  // if they are lookin at an object
  if (ob) 
  {
    if (ITEM_TYPE(ob) != ITEM_CONTAINER || IS_CORPSE(ob))
    {
      act("It would be difficult to trap $p.",FALSE,ch,ob,0,TO_CHAR);
      return;
    }

    act("You examine $p.",FALSE,ch,ob,0,TO_CHAR);
    act("$n examines $p.",TRUE,ch,ob,0,TO_ROOM);

    // hmm, before we place, what if there is a trap there?
    if (TRAPS(ob))
    {
      int prob = 101;
      if (TRAPFLAGGED(TRAPS(ob), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
        prob *= 2;

      if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
      {
        triptrap(TRAPS(ob), ch, FALSE);
        TRAPS(ob) = trap_from_obj(TRAPS(ob), ob);
      }
      else
        act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
      return;
    } 

    if (GET_SKILL(ch, SKILL_LIGHTNINGTRAP) < number(1, 101))
    {
      act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
      return;
    }
  
    // else, place the trap...
    antiremove = (GET_TRAP_LEVEL(ch) >= 65);
    antidetect = (GET_TRAP_LEVEL(ch) >= 70);

    CREATE(t, trap, 1);
    t->ch    = 0;		// design decision to NOT assign this
    t->level = GET_TRAP_LEVEL(ch);
    t->assassin = IS_ASSASSIN(ch);
    t->timer = 0;
  
    t->type = TRT_LIGHTNING;
    FLAGTRAP(t, TRF_ARMED);
    if (antiremove)
      FLAGTRAP(t, TRF_ANTIREMOVE);
    if (antidetect)
      FLAGTRAP(t, TRF_ANTIDETECT);

    trap_to_obj(t, ob);

    act("You successfully place the lightning trap on $p.",FALSE,ch,ob,0,TO_CHAR);
    return;
  }

  // else they mean a direction...
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: lightningtrap <object | [direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to place a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  d = EXIT(ch, dir);

  // hmm, before we place, what if there is a trap there?
  if (TRAPS(d))
  {
    int prob = 101;
    if (TRAPFLAGGED(TRAPS(d), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
    {
      triptrap(TRAPS(d), ch, FALSE);
      TRAPS(d) = trap_from_dir(TRAPS(d), d);
    }
    else
      act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
    return;
  } 

  if (GET_SKILL(ch, SKILL_LIGHTNINGTRAP) < number(1, 101) || EXIT_FLAGGED(d, EX_NOTRAP))
  {
    act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }
  
  // else, place the trap...
  antiremove = (GET_TRAP_LEVEL(ch) >= 65);
  antidetect = (GET_TRAP_LEVEL(ch) >= 70);

  CREATE(t, trap, 1);
  t->ch    = 0;		// design decision to NOT assign this
  t->level = GET_TRAP_LEVEL(ch);
  t->assassin = IS_ASSASSIN(ch);
  t->timer = 0;
  
  t->type = TRT_FIRE;
  FLAGTRAP(t, TRF_ARMED);
  if (antiremove)
    FLAGTRAP(t, TRF_ANTIREMOVE);
  if (antidetect)
    FLAGTRAP(t, TRF_ANTIDETECT);

  trap_to_dir(t, d);

  act("You successfully place the lightning trap.",FALSE,ch,0,0,TO_CHAR);
}

// place a trap on an exit that does level / 5 piercing damage to who trips it
// and ALSO attempts to poison tripper 4/19/98 -jtrhone
ACMD(do_poisontrap)
{
  char *argu = argument;
  int dir;
  rmdirdata *d;
  BOOL antidetect, antiremove;
  trap *t;

  if (IS_NPC(ch))
    return; // for now

  if (!GET_SKILL(ch, SKILL_POISONTRAP))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }
  
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  // they are lookin at a direction
  skip_spaces(&argu);
  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: poisontrap <[direction]>.\n\r",ch);  
    return;
  }

  if (!EXIT(ch, dir) || !EXIT_ISDOOR(EXIT(ch, dir)))
  {
    act("It would be fairly difficult to place a trap there.",TRUE,ch,0,0,TO_CHAR);
    return;
  }

  sprintf(buf, "You examine the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n examines the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  d = EXIT(ch, dir);

  // hmm, before we place, what if there is a trap there?
  if (TRAPS(d))
  {
    int prob = 101;
    if (TRAPFLAGGED(TRAPS(d), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_DETECTTRAP) < number(1, prob))
    {
      triptrap(TRAPS(d), ch, FALSE);
      TRAPS(d) = trap_from_dir(TRAPS(d), d);
    }
    else
      act("You sense an existing trap there!",FALSE,ch,0,0,TO_CHAR);
    return;
  } 

  if (GET_SKILL(ch, SKILL_POISONTRAP) < number(1, 101) || EXIT_FLAGGED(d, EX_NOTRAP))
  {
    act("You fail to place a trap there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }
  
  // else, place the trap...
  antiremove = (GET_TRAP_LEVEL(ch) >= 65);
  antidetect = (GET_TRAP_LEVEL(ch) >= 70);

  CREATE(t, trap, 1);
  t->ch    = 0;		// design decision to NOT assign this
  t->level = GET_TRAP_LEVEL(ch);
  t->assassin = IS_ASSASSIN(ch);
  t->timer = 0;
  
  t->type = TRT_POISON;
  FLAGTRAP(t, TRF_ARMED);
  if (antiremove)
    FLAGTRAP(t, TRF_ANTIREMOVE);
  if (antidetect)
    FLAGTRAP(t, TRF_ANTIDETECT);

  trap_to_dir(t, d);

  act("You successfully place the poisoned dart trap.",FALSE,ch,0,0,TO_CHAR);
}

// place a snare on a room that does (level / 5)d4 damage to who trips it
// 4/19/98 -jtrhone
ACMD(do_snareset)
{
  BOOL antidetect, antiremove;
  rmdata *r;
  trap *t;

  if (IS_NPC(ch))
    return; // for now

  if (INVALID_ROOM(InRoom(ch)))
    return;

  if (!GET_SKILL(ch, SKILL_SETSNARE))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }
  
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  send_to_char("You examine the area.\n\r",ch);
  act("$n examines the area.",TRUE,ch,0,0,TO_ROOM);

  r = &world[InRoom(ch)];

  // hmm, before we place, what if there is a trap there?
  if (TRAPS(r))
  {
    int prob = 101;
    if (TRAPFLAGGED(TRAPS(r), TRF_ANTIDETECT) && GET_LEVEL(ch) < 65)
      prob *= 2;

    if (GET_SKILL(ch, SKILL_DETECTSNARE) < number(1, prob))
    {
      triptrap(TRAPS(r), ch, FALSE);
      TRAPS(r) = trap_from_room(TRAPS(r), r);
    }
    else
      act("You sense an existing snare here!",FALSE,ch,0,0,TO_CHAR);
    return;
  } 

  if (GET_SKILL(ch, SKILL_SETSNARE) < number(1, 101) || ROOM_FLAGGED2(InRoom(ch), NOSNARE))
  {
    act("You fail to place a snare here.",FALSE,ch,0,0,TO_CHAR);
    return;
  }
  
  // else, place the trap...
  antiremove = (GET_TRAP_LEVEL(ch) >= 65);
  antidetect = (GET_TRAP_LEVEL(ch) >= 70);

  CREATE(t, trap, 1);
  t->ch    = 0;		// design decision to NOT assign this
  t->level = GET_TRAP_LEVEL(ch);
  t->assassin = IS_ASSASSIN(ch);
  t->timer = 0;
  
  t->type = TRT_SNARE;
  FLAGTRAP(t, TRF_ARMED);
  if (antiremove)
    FLAGTRAP(t, TRF_ANTIREMOVE);
  if (antidetect)
    FLAGTRAP(t, TRF_ANTIDETECT);

  trap_to_room(t, r);

  act("You successfully place the snare.",FALSE,ch,0,0,TO_CHAR);
}

ACMD(do_subdue)
{
  char *argu = argument;
  chdata *vict;

  if (IS_NPC(ch)) return;

  if (!GET_SKILL(ch, SKILL_SUBDUE))
  {
    send_to_char("You know not of that skill, adventurer.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu || !(vict = get_char_room_vis(ch, argu)))
    if (!(vict = FIGHTING(ch)))
    {
      send_to_char("Subdue who?\n\r",ch);
      return;
    }

  if (!check_mortal_combat(ch, vict))
    return;

  if (GET_SKILL(ch, SKILL_SUBDUE) > number(1, 101))
  {
    // vict makes skill throw every round of combat to remove CH_SUBDUED flag... or
    // it's removed when character stops fighting...
    if (GET_DEX(ch) >= GET_DEX(vict))
      SET_BIT(CHAR_FLAGS(vict), CH_SUBDUED | CH_STUNTOUCHED);

    VWAIT(ch) = number(1, 3);
    VWAIT(vict) = VWAIT(ch) + 1;
  
    act("You subdue $N.", FALSE, ch,0,vict,TO_CHAR);
    act("You have been subdued by $n.", TRUE, ch,0,vict, TO_VICT);
    act("$n subdues $N.", TRUE, ch,0,vict, TO_NOTVICT);

    hit(vict, ch, TYPE_UNDEFINED, TRUE);
  }
  else
  {
    act("You failed to subdue $N.", FALSE, ch,0,vict,TO_CHAR);
    act("$n tried to subdue you, but $e failed.", TRUE, ch,0,vict, TO_VICT);
    act("$n failed to subdue $N.", TRUE, ch,0,vict, TO_NOTVICT);

    hit(vict, ch, TYPE_UNDEFINED, TRUE);
    return;
  }

  // if vict is a mob, make them forget the thief...
  if (ch && vict)
  if (IS_NPC(vict) && GET_SKILL(ch, SKILL_SUBDUE) > number(1, 101))
    if (GET_DEX(ch) >= GET_DEX(vict))
      forget(vict, ch);
}

ACMD(do_redirect)
{   
   chdata *victim;

   if (!IS_THIEF(ch) && !IS_RANGER(ch)) 
   {
      send_to_char("You know not of that skill.\n\r",ch);
      return;
   }

   if (!(victim = FIGHTING(ch)))
   {
     send_to_char("You need to be fighting to redirect.\n\r",ch);
     return;
   }

   if ((GET_SKILL(ch, SKILL_REDIRECT) < number(0, 101)) || !number(0, 9))
   {
     act("You fail to redirect $N's attack.", FALSE, ch, 0, victim, TO_CHAR);
     act("$n failed to redirect $N's attack.", TRUE, ch, 0, victim, TO_NOTVICT);
     WAIT_STATE(ch, 3*PULSE_VIOLENCE);
   }
   else
   {
     act("You redirect $N's attack!", FALSE, ch, 0, victim, TO_CHAR);
     act("$n redirected your attack, OUCH!", FALSE, ch, 0, victim, TO_VICT);
     act("$n redirects $N's attack!", TRUE, ch, 0, victim, TO_NOTVICT);
     WAIT_STATE(ch, 2*PULSE_VIOLENCE);
     SET_BIT(CHAR_FLAGS(victim), CH_REDIRECTED);
   }
}

ACMD(do_sneak)
{
   AffType af;
   byte percent;

   send_to_char("Ok, you'll try to move silently for a while.\n\r", ch);
   if (IS_AFFECTED(ch, AFF_SNEAK))
      affect_from_char(ch, SKILL_SNEAK);

   percent = number(1, 101); /* 101% is a complete failure */

   if (percent > GET_SKILL(ch, SKILL_SNEAK) + dex_app_skill[GET_DEX(ch)].sneak)
      return;

   NewAffect(&af);
   af.type = SKILL_SNEAK;
   af.duration = GET_LEVEL(ch);
   af.bitvector = AFF_SNEAK;
   affect_to_char(ch, &af);
}

ACMD(do_hide)
{
   byte percent;

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

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

   percent = number(1, 101); /* 101% is a complete failure */

   if (percent > GET_SKILL(ch, SKILL_HIDE) + dex_app_skill[GET_DEX(ch)].hide)
      return;
   send_to_char("You hide in the shadows.\n\r",ch);
   SET_BIT(ch->specials.affected_by, AFF_HIDE);
}

ACMD(do_steal)
{
   chdata *victim;
   obdata *obj;
   char	victim_name[240];
   char	obj_name[240];
   int	percent, gold, eq_pos;
   BOOL ohoh = FALSE;

   half_chop(argument, obj_name, victim_name);

   if (!*obj_name || !*victim_name || !(victim = get_char_room_vis(ch, victim_name))) 
   {
      send_to_char("Steal what from who?\n\r", ch);
      return;
   } 

   if (victim == ch) 
   {
      send_to_char("Come on now, that's rather stupid!\n\r", ch);
      return;
   }

   if (IS_NPC(ch) && CHARMED(ch))
   {
     send_to_char("You cannot force yourself to do that!\n\r",ch);
     return;
   }

   /* 101% is a complete failure */
   percent = number(1, 101) - dex_app_skill[GET_DEX(ch)].p_pocket;

   if (!check_mortal_combat(ch, victim))
     return;

   if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
   {
     send_to_char("This area is magically designated SAFE from all assassin actions.\n\r",ch);
     return;
   }

   if (GET_POS(victim) < POS_SLEEPING)
      percent = -1; /* ALWAYS SUCCESS */

   /* NO NO With Imp's and Shopkeepers! */
   if ((IS_IMMORTAL(victim)) || (SPC_FLAGGED(victim, SPC_SHOPKEEP)))
      percent = 101; /* Failure */

   if (str_cmp(obj_name, currency_name) && str_cmp(obj_name, currency_name_plural)) 
   {
      if (!(obj = get_obj_in_list_vis(victim, obj_name, victim->carrying))) 
      {
	 for (eq_pos = 0; (eq_pos < MAX_WEAR); eq_pos++)
	    if (EQ(victim, eq_pos) && (isname(obj_name, EQ(victim, eq_pos)->name)) && 
	        CAN_SEE_OBJ(ch, EQ(victim, eq_pos))) 
            {
	       obj = EQ(victim, eq_pos);
	       break;
	    }

	 if (!obj) 
         {
	    act("$E hasn't got that item.", FALSE, ch, 0, victim, TO_CHAR);
	    return;
	 } 
         else 
         { /* It is equipment */
	    if ((GET_POS(victim) > POS_STUNNED)) 
            {
	       send_to_char("Steal the equipment now?  Impossible!\n\r", ch);
	       return;
	    } 
            else 
            {
	       act("You unequip $p and steal it.", FALSE, ch, obj , 0, TO_CHAR);
	       act("$n steals $p from $N.", FALSE, ch, obj, victim, TO_NOTVICT);
	       obj_to_char(unequip_char(victim, eq_pos, TRUE), ch);
	    }
	 }
      } 
      else 
      {  /* obj found in inventory */

	 percent += GET_OBJ_WEIGHT(obj); /* Make heavy harder */

	 if (AWAKE(victim) && (percent > GET_SKILL(TRUE_CHAR(ch), SKILL_STEAL))) 
         {
	    ohoh = TRUE;
	    act("Oops..", FALSE, ch, 0, 0, TO_CHAR);
	    act("$n tried to steal something from you!", FALSE, ch, 0, victim, TO_VICT);
	    act("$n tries to steal something from $N.", TRUE, ch, 0, victim, TO_NOTVICT);
	 } 
         else 
         { /* Steal the item */
	    if ((IS_CARRYING_N(ch) + 1 < CAN_CARRY_N(ch))) 
            {
	       if ((IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj)) < CAN_CARRY_W(ch)) 
               {
		  obj_from_char(obj);
		  obj_to_char(obj, ch);
		  send_to_char("Got it!\n\r", ch);
	       }
	    } 
            else
	      send_to_char("You cannot carry that much.\n\r", ch);
	 }
      }
   } 
   else 
   { /* Steal some coins */
      if (AWAKE(victim) && (percent > GET_SKILL(TRUE_CHAR(ch), SKILL_STEAL))) 
      {
	 ohoh = TRUE;
	 act("Oops..", FALSE, ch, 0, 0, TO_CHAR);
	 act("You discover that $n has $s hands in your wallet.", FALSE, ch, 0, victim, TO_VICT);

         sprintf(buf, "$n tries to steal %s from $N.", currency_name_plural);
	 act(buf, TRUE, ch, 0, victim, TO_NOTVICT);
      } 
      else 
      {
	 /* Steal some gold coins */
	 gold = (int) ((GET_GOLD(victim) * number(1, 10)) / 100);
	 gold = MIN(178, gold);

	 if (gold > 0) 
         {
	    GET_GOLD(ch) += gold;
	    GET_GOLD(victim) -= gold;
	    sprintf(buf, "Bingo!  You got %d %s.\n\r", gold, currency_name_plural);
	    S2C();
	 } 
         else 
	    send_to_char("You couldn't get anything...\n\r", ch);
      }
   }

   if (ohoh && IS_NPC(victim) && AWAKE(victim))
      if (MOB_FLAGGED(victim, MOB_NICE_THIEF)) 
      {
	 sprintf(buf, "%s is a bloody thief!", GET_NAME(ch));
	 do_gen_com(victim, buf, 0, SCMD_YELL);
	 log(buf);
	 send_to_char("Don't you ever do that again!\n\r", ch);
      } 
      else
	 hit(victim, ch, TYPE_UNDEFINED, TRUE);
}

ACMD(do_pick)
{
   byte percent;
   int	door, other_room;
   char	type[MAX_INPUT_LENGTH], dir[MAX_INPUT_LENGTH];
   rmdirdata *back;
   obdata *obj;
   chdata *victim;

   two_arguments(argument, type, dir);

   percent = number(1, 101); /* 101% is a complete failure */

   if (percent > GET_SKILL(ch, SKILL_PICK_LOCK)) {
      send_to_char("You failed to pick the lock.\n\r", ch);
      return;
   }

   if (!*type)
      send_to_char("Pick what?\n\r", ch);
   else if (generic_find(argument, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj))

      /* this is an object */

      if (obj->type_flag != ITEM_CONTAINER)
	 send_to_char("That's not a container.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_CLOSED))
	 send_to_char("Silly - it isn't even closed!\n\r", ch);
      else if (obj->value[2] < 0)
	 send_to_char("Odd - you can't seem to find a keyhole.\n\r", ch);
      else if (!IS_SET(obj->value[1], CONT_LOCKED))
	 send_to_char("Oho! This thing is NOT locked!\n\r", ch);
      else if (IS_SET(obj->value[1], CONT_PICKPROOF))
	 send_to_char("It resists your attempts at picking it.\n\r", ch);
      else {
	 REMOVE_BIT(obj->value[1], CONT_LOCKED);
	 send_to_char("*Click*\n\r", ch);
	 act("$n fiddles with $p.", FALSE, ch, obj, 0, TO_ROOM);
      }
   else if ((door = find_door(ch, type, dir)) >= 0)
      if (!EXIT_ISDOOR(EXIT(ch, door)))
	 send_to_char("That's absurd.\n\r", ch);
      else if (EXIT_OPEN(EXIT(ch, door)))
	 send_to_char("You realize that the door is already open.\n\r", ch);
      else if (EXIT(ch, door)->key < 0)
	 send_to_char("You can't seem to spot any lock to pick.\n\r", ch);
      else if (EXIT_UNLOCKED(EXIT(ch, door)))
	 send_to_char("Oh.. it wasn't locked at all.\n\r", ch);
      else if (EXIT_FLAGGED(EXIT(ch, door), EX_PICKPROOF))
	 send_to_char("You are unable to pick this lock.\n\r", ch);
      else 
      {
	 UNFLAG_EXIT(EXIT(ch, door), EX_LOCKED);
	 if (EXIT(ch, door)->keyword)
	    act("$n skillfully picks the lock of the $F.", 0, ch, 0,
	        EXIT(ch, door)->keyword, TO_ROOM);
	 else
	    act("$n picks the lock.", TRUE, ch, 0, 0, TO_ROOM);
	 send_to_char("The lock quickly yields to your skills.\n\r", ch);
	 /* now for unlocking the other side, too */
	 if ((other_room = EXIT(ch, door)->to_room) != NOWHERE)
	    if ((back = world[other_room].dir_option[rev_dir[door]]))
	       if (back->to_room == ch->in_room)
		  REMOVE_BIT(back->exinfo, EX_LOCKED);
      }
}

ACMD(do_circle)
{
   chdata *victim;
   byte percent, prob;
   
   one_argument(argument, arg);
   
   if (!IS_THIEF(ch) && !IS_BARD(ch)) 
   {
      send_to_char("You may not use that form of attack.\n\r",ch);
      return;
   }
   
   if (!(victim = get_char_room_vis(ch, arg))) {
      if (FIGHTING(ch)) {
	 victim = FIGHTING(ch);
      } else {
	 send_to_char("You must be fighting in order to use that form of attack.\n\r",ch);
	 return;
      }
   }
   
   if (victim != FIGHTING(ch)) {
      send_to_char("You must be fighting the person you intend to circle.\n\r",ch);
      return;
   }
   
   if (ch == FIGHTING(victim)) {
      send_to_char("You must not be in the lead to use that form of attack.\n\r",ch);
      return;
   }
   
   if (!EQ(ch, W_WIELD)) {
      send_to_char("You need to wield a weapon to make it a success.\n\r", ch);
      return;
   }

   if (EQ(ch, W_WIELD)->value[3] != TYPE_BACKSTAB - TYPE_HIT) {
      send_to_char("Only piercing weapons can be used for circling.\n\r", ch);
      return;
   }
   
   percent = number(1, 101); /* 101% is a complete failure */

   prob = GET_SKILL(ch, SKILL_CIRCLE);

   if (AWAKE(victim) && (percent > prob))
      damage(ch, victim, 0, SKILL_CIRCLE, TRUE);
   else
      hit(ch, victim, SKILL_CIRCLE, FALSE);

   WAIT_STATE(ch, PULSE_VIOLENCE * 2);
}

ACMD(do_backstab)
{
  obdata *w;
  chdata *victim;
  byte percent, prob;

  if (IS_PC(ch) && (GET_LEVEL(ch) == LEV_IMM || GET_LEVEL(ch) == LEV_GOD))
  {
     send_to_char("You, being an Immortal, are forbidden to kill.\n\r",ch);
     return;
  }

  if (!IS_THIEF(ch))
  {
    send_to_char("You don't know how to backstab.\n\r",ch);
    return;
  }

  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  one_argument(argument, buf);

  if (!(victim = get_char_room_vis(ch, buf))) {
     send_to_char("Backstab who?\n\r", ch);
     return;
  }

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

  if (!check_mortal_combat(ch, victim))
    return;

  if (!(w = EQ(ch, W_WIELD)))
  {
     send_to_char("You need to wield a weapon to make it a success.\n\r", ch);
     return;
  }

  if (w->value[3] != (TYPE_BACKSTAB - TYPE_HIT)) {
     send_to_char("Only piercing weapons can be used for backstabbing.\n\r", ch);
     return;
  }

  if (FIGHTING(victim)) 
  {
     send_to_char("You can't backstab a fighting person, too alert!\n\r", ch);
     return;
  }

  if (!check_truce(ch, victim))
    return;

  percent = number(1, 101); /* 101% is a complete failure */

  prob = GET_SKILL(ch, SKILL_BACKSTAB);

  if (AWAKE(victim) && (percent > prob))
     damage(ch, victim, 0, SKILL_BACKSTAB, TRUE);
  else
     hit(ch, victim, SKILL_BACKSTAB, TRUE);
}

// level 70 thief skill...  4/26/98 -jtrhone
ACMD(do_assassinate)
{
  obdata *w;
  chdata *victim;
  int success, roll, stat;

  if (IS_PC(ch) && (GET_LEVEL(ch) == LEV_IMM || GET_LEVEL(ch) == LEV_GOD))
  {
     send_to_char("You, being an Immortal, are forbidden to kill.\n\r",ch);
     return;
  }

  if (!GET_SKILL(ch, SKILL_ASSASSINATE))
  {
    send_to_char("Alas, you would only end up hurting yourself.\n\r",ch);
    return;
  }

  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

  one_argument(argument, buf);

  if (!(victim = get_char_room_vis(ch, buf))) {
     send_to_char("Assassinate who?\n\r", ch);
     return;
  }

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

  if (!check_mortal_combat(ch, victim))
    return;

  if (!(w = EQ(ch, W_WIELD)))
  {
     send_to_char("You need to wield a weapon to make it a success.\n\r", ch);
     return;
  }

  if (w->value[3] != (TYPE_BACKSTAB - TYPE_HIT)) {
     send_to_char("Only piercing weapons can be used for assassinations.\n\r", ch);
     return;
  }

  if (FIGHTING(victim)) 
  {
     send_to_char("You can't assassinate a fighting person, too alert!\n\r", ch);
     return;
  }

  if (!check_truce(ch, victim))
    return;

  // regardless... wait state
  WAIT_STATE(ch, PULSE_VIOLENCE*5);

  // have to roll against yer dex
  stat = MAX(GET_DEX(ch), GET_CON(ch));
  roll = 5 + (stat - 15);
  roll = MAX(roll, 1);

  // if victim has higher dex... no bonus
  if (GET_DEX(victim) >= stat)
    roll = 5;

  if (!AWAKE(victim))
    roll = 9;

  success = (roll > number(1, 10));

  if (!success || GET_SKILL(ch, SKILL_ASSASSINATE) < number(1, 101))
  {
    act("You fail to assassinate $N!.", FALSE, ch, 0, victim, TO_CHAR);
    act("$n failed to assassinate you!.", FALSE, ch, 0, victim, TO_VICT);
    act("$n failed to assassinate $N!.", TRUE, ch, 0, victim, TO_NOTVICT);

    hit(victim, ch, TYPE_UNDEFINED, TRUE);
    return;
  }

  act("%B%1You attempt to assassinate $N!.%0", FALSE, ch, 0, victim, TO_CHAR);
  act("%B%1$n attempts to assassinate you!%0.", FALSE, ch, 0, victim, TO_VICT);
  act("%B%1$n attempts to assassinate $N!.%0", TRUE, ch, 0, victim, TO_NOTVICT);

  GET_HIT(victim) /= 2;

  // victim will retaliate...
  hit(victim, ch, TYPE_UNDEFINED, TRUE);
}