/*
  SillyMUD Distribution V1.1b             (c) 1993 SillyMUD Developement
 
  See license.doc for distribution terms.   SillyMUD is based on DIKUMUD
*/

#include <stdio.h>

#include "protos.h"

int choose_exit(int in_room, int tgt_room, int dvar);
struct room_data *real_roomp(int);
int remove_trap( struct char_data *ch, struct obj_data *trap);

extern char *dirs[];
extern struct char_data *character_list;
extern struct room_data *world;
extern struct dex_app_type dex_app[];

struct hunting_data {
  char	*name;
  struct char_data **victim;
};


/*************************************/
/* predicates for find_path function */

int is_target_room_p(int room, void *tgt_room);

int named_object_on_ground(int room, void *c_data);

/* predicates for find_path function */
/*************************************/


/*
**  Disarm:
*/

void do_disarm(struct char_data *ch, char *argument, int cmd)
{
  char name[30];
  int percent;
  struct char_data *victim;
  struct obj_data *w, *trap;

  if (!ch->skills) return;

  if (check_peaceful(ch,"You feel too peaceful to contemplate violence.\n\r"))
    return;

  if (!IS_PC(ch) && cmd)
    return;
  
  /*
   *   get victim
   */
  only_argument(argument, name);
    if (!(victim = get_char_room_vis(ch, name))) {
    if (ch->specials.fighting) {
      victim = ch->specials.fighting;
    } else {
      if (!ch->skills[SKILL_REMOVE_TRAP].learned) {
	send_to_char("Disarm who?\n\r", ch);
	return;
      } else {

	if (MOUNTED(ch)) {
	  send_to_char("Yeah... right... while mounted\n\r", ch);
	  return;
	}

	if (!(trap = get_obj_in_list_vis(ch, name, 
		    real_roomp(ch->in_room)->contents))) {
	  if (!(trap = get_obj_in_list_vis(ch, name, ch->carrying))) {
	    send_to_char("Disarm what?\n\r", ch);
	    return;
	  }
	}
	if (trap) {
	  remove_trap(ch, trap);
	  return;
	}
      }
    }
  }
  
  
  if (victim == ch) {
    send_to_char("Aren't we funny today...\n\r", ch);
    return;
  }

  if (victim != ch->specials.fighting) {
    send_to_char("but you aren't fighting them!\n\r", ch);
    return;
  }

  if (ch->attackers > 3) {
    send_to_char("There is no room to disarm!\n\r", ch);
    return;
  }

  if (!HasClass(ch, CLASS_WARRIOR) && !HasClass(ch, CLASS_MONK)) {
    send_to_char("You're no warrior!\n\r", ch);
    return;
  }
  
  /*
   *   make roll - modified by dex && level
   */
  percent=number(1,101); /* 101% is a complete failure */
  
  percent -= dex_app[GET_DEX(ch)].reaction*10;
  percent += dex_app[GET_DEX(victim)].reaction*10;
  if (!ch->equipment[WIELD] && !HasClass(ch, CLASS_MONK)) {
    percent += 50;
  }

  percent += GetMaxLevel(victim);
  if (HasClass(victim, CLASS_MONK))
    percent += GetMaxLevel(victim);

  if (HasClass(ch, CLASS_MONK)) {
    percent -= GetMaxLevel(ch);
  } else {
    percent -= GetMaxLevel(ch)>>1;
  }

  if (percent > ch->skills[SKILL_DISARM].learned) {
    /*
     *   failure.
     */
    act("You try to disarm $N, but fail miserably.", 
	TRUE, ch, 0, victim, TO_CHAR);
    act("$n does a nifty fighting move, but then falls on $s butt.",
	TRUE, ch, 0, 0, TO_ROOM);
    GET_POS(ch) = POSITION_SITTING;
    if ((IS_NPC(victim)) && (GET_POS(victim) > POSITION_SLEEPING) &&
        (!victim->specials.fighting)) {
      set_fighting(victim, ch);
    }
    LearnFromMistake(ch, SKILL_DISARM, 0, 95);
    WAIT_STATE(ch, PULSE_VIOLENCE*3);
  } else {
    /*
     *  success
     */
    if (victim->equipment[WIELD]) {
      w = unequip_char(victim, WIELD);
      act("$n makes an impressive fighting move.", 
	  TRUE, ch, 0, 0, TO_ROOM);
      act("You send $p flying from $N's grasp.", TRUE, ch, w, victim, 
	  TO_CHAR);
      act("$p flies from your grasp.", TRUE, ch, w, victim, TO_VICT);
/*
  send the object to a nearby room, instead
*/
      obj_to_room(w, victim->in_room);
    } else {
      act("You try to disarm $N, but $E doesn't have a weapon.", 
	  TRUE, ch, 0, victim, TO_CHAR);
      act("$n makes an impressive fighting move, but does little more.",
	  TRUE, ch, 0, 0, TO_ROOM);
    }
    if ((IS_NPC(victim)) && (GET_POS(victim) > POSITION_SLEEPING) &&
        (!victim->specials.fighting)) {
      set_fighting(victim, ch);
    }
    WAIT_STATE(victim, PULSE_VIOLENCE*2);
    WAIT_STATE(ch, PULSE_VIOLENCE*2);
  }  
}


/*
**   Track:
*/

int named_mobile_in_room(int room, struct hunting_data *c_data)
{
  struct char_data	*scan;

  for (scan = real_roomp(room)->people; scan; scan = scan->next_in_room)
    if (isname(c_data->name, scan->player.name)) {
         *(c_data->victim) = scan;
         return 1;
  }
  return 0;
}

void do_track(struct char_data *ch, char *argument, int cmd)
{
   char name[256], buf[256], found=FALSE;
   int dist, code;
  struct hunting_data	huntd;
   struct char_data *scan;
  extern struct char_data  *character_list;

#if NOTRACK
   send_to_char("Sorry, tracking is disabled. Try again after reboot.\n\r",ch);
   return;
#endif

  only_argument(argument, name);

  found = FALSE;
  for (scan = character_list; scan; scan = scan->next)
    if (isname(name, scan->player.name)) {
         found = TRUE;
    }

  if (!found) {
    send_to_char("You are unable to find traces of one.\n\r", ch);
    return;
  }

   if (!ch->skills) 
     dist = 10;
   else
     dist = ch->skills[SKILL_HUNT].learned;


   if (IS_SET(ch->player.class, CLASS_THIEF)) {
     dist *= 3;
   }

   switch(GET_RACE(ch)){
   case RACE_ELVEN:
     dist *= 2;               /* even better */
     break;
   case RACE_DEVIL:
   case RACE_DEMON:
     dist = MAX_ROOMS;   /* as good as can be */
     break;
   default:
     break;
   }

  if (GetMaxLevel(ch) >= IMMORTAL)
    dist = MAX_ROOMS;
 

  if (affected_by_spell(ch, SPELL_MINOR_TRACK)) {
    dist = GetMaxLevel(ch) * 50;
  } else if (affected_by_spell(ch, SPELL_MAJOR_TRACK)){
    dist = GetMaxLevel(ch) * 100;
  }

  if (dist == 0)
    return;
 
  ch->hunt_dist = dist;

  ch->specials.hunting = 0;
  huntd.name = name;
  huntd.victim = &ch->specials.hunting;

  if ((GetMaxLevel(ch) < MIN_GLOB_TRACK_LEV) ||
      (affected_by_spell(ch, SPELL_MINOR_TRACK)) || 
      (affected_by_spell(ch, SPELL_MAJOR_TRACK))) {
     code = find_path( ch->in_room, named_mobile_in_room, &huntd, -dist, 1);
  } else {
     code = find_path( ch->in_room, named_mobile_in_room, &huntd, -dist, 0);
  }
  
   WAIT_STATE(ch, PULSE_VIOLENCE*1);

   if (code == -1) {
    send_to_char("You are unable to find traces of one.\n\r", ch);
     return;
   } else {
     if (IS_LIGHT(ch->in_room)) {
        SET_BIT(ch->specials.act, PLR_HUNTING);
       sprintf(buf, "You see traces of your quarry to the %s\n\r", dirs[code]);
        send_to_char(buf,ch);
      } else {
      ch->specials.hunting = 0;
	send_to_char("It's too dark in here to track...\n\r",ch);
	return;
      }
   }
 }

int track( struct char_data *ch, struct char_data *vict)
{

  char buf[256];
  int code;

  if ((!ch) || (!vict))
    return(-1);

  if ((GetMaxLevel(ch) < MIN_GLOB_TRACK_LEV) || 
      (affected_by_spell(ch, SPELL_MINOR_TRACK)) || 
      (affected_by_spell(ch, SPELL_MAJOR_TRACK))) {
     code = choose_exit_in_zone(ch->in_room, vict->in_room, ch->hunt_dist);
  } else {
     code = choose_exit_global(ch->in_room, vict->in_room, ch->hunt_dist);
  }
  if ((!ch) || (!vict))
    return(-1);


  if (ch->in_room == vict->in_room) {
    send_to_char("##You have found your target!\n\r",ch);
    return(FALSE);  /* false to continue the hunt */
  }
  if (code == -1) {
    send_to_char("##You have lost the trail.\n\r",ch);
    return(FALSE);
  } else {
    sprintf(buf, "##You see a faint trail to the %s\n\r", dirs[code]);
    send_to_char(buf, ch);
    return(TRUE);
  }

}

int dir_track( struct char_data *ch, struct char_data *vict)
{

  char buf[256];
  int code;

  if ((!ch) || (!vict))
    return(-1);

  
  if ((GetMaxLevel(ch) >= MIN_GLOB_TRACK_LEV) ||
      (affected_by_spell(ch, SPELL_MINOR_TRACK)) || 
      (affected_by_spell(ch, SPELL_MAJOR_TRACK))) {
    code = choose_exit_global(ch->in_room, vict->in_room, ch->hunt_dist);
  } else {
    code = choose_exit_in_zone(ch->in_room, vict->in_room, ch->hunt_dist);
  }
  if ((!ch) || (!vict))
    return(-1);

  if (code == -1) {
    if (ch->in_room == vict->in_room) {
      send_to_char("##You have found your target!\n\r",ch);
    } else {
      send_to_char("##You have lost the trail.\n\r",ch);
    }
    return(-1);  /* false to continue the hunt */
  } else {
    sprintf(buf, "##You see a faint trail to the %s\n\r", dirs[code]);
    send_to_char(buf, ch);
    return(code);
  }

}




/** Perform breadth first search on rooms from start (in_room) **/
/** until end (tgt_room) is reached. Then return the correct   **/
/** direction to take from start to reach end.                 **/

/* thoth@manatee.cis.ufl.edu
   if dvar<0 then search THROUGH closed but not locked doors,
   for mobiles that know how to open doors.
 */

#define IS_DIR    (real_roomp(q_head->room_nr)->dir_option[i])
#define GO_OK  (!IS_SET(IS_DIR->exit_info,EX_CLOSED)\
		 && (IS_DIR->to_room != NOWHERE))
#define GO_OK_SMARTER  (!IS_SET(IS_DIR->exit_info,EX_LOCKED)\
		 && (IS_DIR->to_room != NOWHERE))

void donothing()
{
  return;
}

int find_path(int in_room, int (*predicate)(), void *c_data, 
	      int depth, int in_zone)
{
   struct room_q *tmp_q, *q_head, *q_tail;
#if 1
  struct hash_header	x_room;
/*  static struct hash_header	x_room; */
#else
  struct nodes x_room[MAX_ROOMS];
#endif
   int i, tmp_room, count=0, thru_doors;
  struct room_data	*herep, *therep;
  struct room_data      *startp;
  struct room_direction_data	*exitp;

	/* If start = destination we are done */
   if ((predicate)(in_room, c_data))
     return -1;

#if 0
   if (top_of_world > MAX_ROOMS) {
     log("TRACK Is disabled, too many rooms.\n\rContact Loki soon.\n\r");
    return -1;
   }
#endif

   if (depth<0) {
     thru_doors = TRUE;
     depth = - depth;
   } else {
     thru_doors = FALSE;
   }

  startp = real_roomp(in_room);

  init_hash_table(&x_room, sizeof(int), 2048);
  hash_enter(&x_room, in_room, (void*)-1);

	   /* initialize queue */
   q_head = (struct room_q *) malloc(sizeof(struct room_q));
   q_tail = q_head;
   q_tail->room_nr = in_room;
   q_tail->next_q = 0;

  while(q_head) {
    herep = real_roomp(q_head->room_nr);
		/* for each room test all directions */
    if (herep->zone == startp->zone || !in_zone) {  
                                           /* only look in this zone.. 
					      saves cpu time.  makes world
					      safer for players
					      */
      for(i = 0; i <= 5; i++) {
        exitp = herep->dir_option[i];
        if (exit_ok(exitp, &therep) && (thru_doors ? GO_OK_SMARTER : GO_OK)) {
	  /* next room */
	  tmp_room = herep->dir_option[i]->to_room;
	  if(!((predicate)(tmp_room, c_data))) {
	    /* shall we add room to queue ? */
	    /* count determines total breadth and depth */
	    if(!hash_find(&x_room,tmp_room) && (count < depth)
	       && !IS_SET(RM_FLAGS(tmp_room),DEATH)) {
	      count++;
	      /* mark room as visted and put on queue */
	      
	      tmp_q = (struct room_q *) malloc(sizeof(struct room_q));
	      tmp_q->room_nr = tmp_room;
	      tmp_q->next_q = 0;
	      q_tail->next_q = tmp_q;
	      q_tail = tmp_q;
	      
	      /* ancestor for first layer is the direction */
	      hash_enter(&x_room, tmp_room,
			 ((int)hash_find(&x_room,q_head->room_nr) == -1) ?
			 (void*)(i+1) : hash_find(&x_room,q_head->room_nr));
	    }
	  } else {
	    /* have reached our goal so free queue */
	    tmp_room = q_head->room_nr;
	    for(;q_head;q_head = tmp_q)   {
	      tmp_q = q_head->next_q;
	      free(q_head);
	    }
	    /* return direction if first layer */
	    if ((int)hash_find(&x_room,tmp_room)==-1) {
              if (x_room.buckets) { /* junk left over from a previous track */
		destroy_hash_table(&x_room, donothing);
              }
	      return(i);
	    } else {  /* else return the ancestor */
	      int i;
	      
              i = (int)hash_find(&x_room,tmp_room);
              if (x_room.buckets) { /* junk left over from a previous track */
		destroy_hash_table(&x_room, donothing);
              }
	      return( -1+i);
	    }
	  }
	}
      }
    }
  
      /* free queue head and point to next entry */
      tmp_q = q_head->next_q;
      free(q_head);
      q_head = tmp_q;
   }
   /* couldn't find path */
   if (x_room.buckets) { /* junk left over from a previous track */
      destroy_hash_table(&x_room, donothing);
   } 
   return(-1);

}


int choose_exit_global(int in_room, int tgt_room, int depth)
{
  return find_path(in_room, is_target_room_p, (void*)tgt_room, depth, 0);
}

int choose_exit_in_zone(int in_room, int tgt_room, int depth)
{
  return find_path(in_room, is_target_room_p, (void*)tgt_room, depth, 1);
}

int go_direction(struct char_data *ch, int dir)     
{
  if (ch->specials.fighting)
    return 0;
  
  if (!IS_SET(EXIT(ch,dir)->exit_info, EX_CLOSED)) {
    do_move(ch, "", dir+1);
  } else if ( IsHumanoid(ch) && !IS_SET(EXIT(ch,dir)->exit_info, EX_LOCKED) ) {
    open_door(ch, dir);
    return 0;
  }
}


void slam_into_wall( struct char_data *ch, struct room_direction_data *exitp)
{
  char doorname[128];
  char buf[256];
  
  if (exitp->keyword && *exitp->keyword) {
    if ((strcmp(fname(exitp->keyword), "secret")==0) ||
	(IS_SET(exitp->exit_info, EX_SECRET))) {
      strcpy(doorname, "wall");
    } else {
      strcpy(doorname, fname(exitp->keyword));
    }
  } else {
    strcpy(doorname, "barrier");
  }
  sprintf(buf, "You slam against the %s with no effect\n\r", doorname);
  send_to_char(buf, ch);
  send_to_char("OUCH!  That REALLY Hurt!\n\r", ch);
  sprintf(buf, "$n crashes against the %s with no effect\n\r", doorname);
  act(buf, FALSE, ch, 0, 0, TO_ROOM);
  GET_HIT(ch) -= number(1, 10)*2;
  if (GET_HIT(ch) < 0)
    GET_HIT(ch) = 0;
  GET_POS(ch) = POSITION_STUNNED;
  return;
}


/*
  skill to allow fighters to break down doors
*/
void do_doorbash( struct char_data *ch, char *arg, int cmd)
{
  extern char *dirs[];
  int dir;
  int ok;
  struct room_direction_data *exitp;
  int was_in, roll;

  char buf[256], type[128], direction[128];

  if (GET_MOVE(ch) < 10) {
    send_to_char("You're too tired to do that\n\r", ch);
    return;
  }

  if (MOUNTED(ch)) {
    send_to_char("Yeah... right... while mounted\n\r", ch);
    return;
  }

  /*
    make sure that the argument is a direction, or a keyword.
  */

  for (;*arg == ' '; arg++);

  argument_interpreter(arg, type, direction);

  if ((dir = find_door(ch, type, direction)) >= 0) {
    ok = TRUE;
  } else {
    act("$n looks around, bewildered.", FALSE, ch, 0, 0, TO_ROOM);
    return;
  }

  if (!ok) {
    send_to_char("Hmm, you shouldn't have gotten this far\n\r", ch);
    return;
  }

  exitp = EXIT(ch, dir);
  if (!exitp) {
    send_to_char("you shouldn't have gotten here.\n\r", ch);
    return;
  }

  if (dir == UP) {
    if (real_roomp(exitp->to_room)->sector_type == SECT_AIR &&
	!IS_AFFECTED(ch, AFF_FLYING)) {
      send_to_char("You have no way of getting there!\n\r", ch);
      return;
    }
  }
  
  sprintf(buf, "$n charges %swards", dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_ROOM);
  sprintf(buf, "You charge %swards\n\r", dirs[dir]);
  send_to_char(buf, ch);

  if (!IS_SET(exitp->exit_info, EX_CLOSED)) {
    was_in = ch->in_room;
    char_from_room(ch);
    char_to_room(ch, exitp->to_room);
    do_look(ch, "", 0);

    DisplayMove(ch, dir, was_in, 1);
    if (!check_falling(ch)) {
      if (IS_SET(RM_FLAGS(ch->in_room), DEATH) && 
	  GetMaxLevel(ch) < LOW_IMMORTAL) {
	NailThisSucker(ch);
	return;
      } else {
	WAIT_STATE(ch, PULSE_VIOLENCE*3);
	GET_MOVE(ch) -= 10;
      }
    } else {
      return;
    }
    WAIT_STATE(ch, PULSE_VIOLENCE*3);
    GET_MOVE(ch) -= 10;
    return;
  }

  GET_MOVE(ch) -= 10;

  if (IS_SET(exitp->exit_info, EX_LOCKED) &&
      IS_SET(exitp->exit_info, EX_PICKPROOF)) {
    slam_into_wall(ch, exitp);
    return;
  }

  /*
    now we've checked for failures, time to check for success;
    */
  if (ch->skills) {
    if (ch->skills[SKILL_DOORBASH].learned) {
      roll = number(1, 100);
      if (roll > ch->skills[SKILL_DOORBASH].learned) {
	slam_into_wall(ch, exitp);
	LearnFromMistake(ch, SKILL_DOORBASH, 0, 95);
      } else {
	/*
	  unlock and open the door
	  */
	sprintf(buf, "$n slams into the %s, and it bursts open!", 
		fname(exitp->keyword));
	act(buf, FALSE, ch, 0, 0, TO_ROOM);
	sprintf(buf, "You slam into the %s, and it bursts open!\n\r", 
		fname(exitp->keyword));
	send_to_char(buf, ch);
	raw_unlock_door(ch, exitp, dir);
	raw_open_door(ch, dir);
	GET_HIT(ch) -= number(1,5);
	/*
	  Now a dex check to keep from flying into the next room
	  */
	roll = number(1, 20);
	if (roll > GET_DEX(ch)) {
	  was_in = ch->in_room;

	  char_from_room(ch);
	  char_to_room(ch, exitp->to_room);
	  do_look(ch, "", 0);
	  DisplayMove(ch, dir, was_in, 1);
	  if (!check_falling(ch)) {
	    if (IS_SET(RM_FLAGS(ch->in_room), DEATH) && 
		GetMaxLevel(ch) < LOW_IMMORTAL) {
	      NailThisSucker(ch);
	      return;
	    }
	  } else {
	    return;
	  }
	  WAIT_STATE(ch, PULSE_VIOLENCE*3);
	  GET_MOVE(ch) -= 10;
	  return;	  
	} else {
	  WAIT_STATE(ch, PULSE_VIOLENCE*1);
	  GET_MOVE(ch) -= 5;
	  return;
	}
      }
    } else {
      send_to_char("You just don't know the nuances of door-bashing.\n\r", ch);
      slam_into_wall(ch, exitp);
      return;
    }
  } else {
    send_to_char("You're just a goofy mob.\n\r", ch);
    return;
  }
}

/*
  skill to allow anyone to move through rivers and underwater
*/

void do_swim( struct char_data *ch, char *arg, int cmd)
{

  struct affected_type af;
  byte percent;
  

  send_to_char("Ok, you'll try to swim for a while.\n\r", ch);

  if (IS_AFFECTED(ch, AFF_WATERBREATH)) {
    /* kinda pointless if they don't need to...*/
    return;
  }
  
  if (affected_by_spell(ch, SKILL_SWIM)) {
    send_to_char("You're too exhausted to swim right now\n", ch);
    return;
  }

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

  if (!ch->skills)
    return;
  
  if (percent > ch->skills[SKILL_SWIM].learned) {
    send_to_char("You're too afraid to enter the water\n\r",ch);
    if (ch->skills[SKILL_SWIM].learned < 95 &&
	ch->skills[SKILL_SWIM].learned > 0) {
      if (number(1,101) > ch->skills[SKILL_SWIM].learned) {
	send_to_char("You feel a bit braver, though\n\r", ch);
	ch->skills[SKILL_SWIM].learned++;
      }
    }
    return;
  }
 
  af.type = SKILL_SWIM;
  af.duration = (ch->skills[SKILL_SWIM].learned/10)+1;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.bitvector = AFF_WATERBREATH;
  affect_to_char(ch, &af);

  af.type = SKILL_SWIM;
  af.duration = 13;
  af.modifier = -10;
  af.location = APPLY_MOVE;
  af.bitvector = 0;
  affect_to_char(ch, &af);

}


int SpyCheck(struct char_data *ch)
{

  if (!ch->skills) return(FALSE);

  if (number(1,101) > ch->skills[SKILL_SPY].learned)
    return(FALSE);

  return(TRUE);

}

void do_spy( struct char_data *ch, char *arg, int cmd)
{

  struct affected_type af;
  byte percent;
  

  send_to_char("Ok, you'll try to act like a secret agent\n\r", ch);

  if (IS_AFFECTED(ch, AFF_SCRYING)) {
    /* kinda pointless if they don't need to...*/
    return;
  }
  
  if (affected_by_spell(ch, SKILL_SPY)) {
    send_to_char("You're already acting like a secret agent\n", ch);
    return;
  }

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

  if (!ch->skills)
    return;
  
  if (percent > ch->skills[SKILL_SPY].learned) {
    if (ch->skills[SKILL_SPY].learned < 95 &&
	ch->skills[SKILL_SPY].learned > 0) {
      if (number(1,101) > ch->skills[SKILL_SPY].learned) {
	ch->skills[SKILL_SPY].learned++;
      }
    }
    af.type = SKILL_SPY;
    af.duration = (ch->skills[SKILL_SPY].learned/10)+1;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = 0;
    affect_to_char(ch, &af);    return;
  }
 
  af.type = SKILL_SPY;
  af.duration = (ch->skills[SKILL_SPY].learned/10)+1;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.bitvector = AFF_SCRYING;
  affect_to_char(ch, &af);    return;
}

int remove_trap( struct char_data *ch, struct obj_data *trap)
{
  int num;

  if (ITEM_TYPE(trap) != ITEM_TRAP) {
    send_to_char("That's no trap!\n\r", ch);
    return(FALSE);
  }
  if (GET_TRAP_CHARGES(trap) <= 0) {
    send_to_char("That trap is already sprung!\n\r", ch);
    return(FALSE);
  }
  num = number(1,101);
  if (num < ch->skills[SKILL_REMOVE_TRAP].learned) {
    send_to_char("<Click>\n\r", ch);
    act("$n disarms $p", FALSE, ch, trap, 0, TO_ROOM);
    GET_TRAP_CHARGES(trap) = 0;
    return(TRUE);
  } else {
    send_to_char("<Click>\n\r(uh oh)\n\r", ch);
    act("$n attempts to disarm $p", FALSE, ch, trap, 0, TO_ROOM);
    TriggerTrap(ch, trap);
    return(TRUE);
  }
}

void do_feign_death( struct char_data *ch, char *arg, int cmd)
{
  struct room_data *rp;
  struct char_data *t;

  if (!ch->skills)
    return;

  if (!ch->specials.fighting) {
    send_to_char("But you are not fighting anything...\n\r", ch);
    return;
  }
  
  if (!HasClass(ch, CLASS_MONK)) {
    send_to_char("You're no monk!\n\r", ch);
    return;
  }

  if (MOUNTED(ch)) {
    send_to_char("Yeah... right... while mounted\n\r", ch);
    return;
  }

  rp = real_roomp(ch->in_room);
  if (!rp)
    return;

  send_to_char("You try to fake your own demise\n\r", ch);

  death_cry(ch);
  act("$n is dead! R.I.P.", FALSE, ch, 0, 0, TO_ROOM);

  if (number(1,101) < ch->skills[SKILL_FEIGN_DEATH].learned) {
    stop_fighting(ch);
    for (t = rp->people;t;t=t->next_in_room) {
      if (t->specials.fighting == ch) {
	stop_fighting(t);
	if (number(1,101) < ch->skills[SKILL_FEIGN_DEATH].learned/2)
	  SET_BIT(ch->specials.affected_by, AFF_HIDE);
	GET_POS(ch) = POSITION_SLEEPING;
      }
    }
    WAIT_STATE(ch, PULSE_VIOLENCE*2);
    return;
  } else {
    GET_POS(ch) = POSITION_SLEEPING;
    WAIT_STATE(ch, PULSE_VIOLENCE*3);
    if (ch->skills[SKILL_FEIGN_DEATH].learned < 95 &&
	ch->skills[SKILL_FEIGN_DEATH].learned > 0) {
      if (number(1,101) > ch->skills[SKILL_FEIGN_DEATH].learned) {
	ch->skills[SKILL_FEIGN_DEATH].learned++;
      }
    }
  }
}


void do_first_aid( struct char_data *ch, char *arg, int cmd)
{
  struct affected_type af;
    
  send_to_char("You attempt to render first aid unto yourself\n\r", ch);

  if (affected_by_spell(ch, SKILL_FIRST_AID)) {
    send_to_char("You can only do this once per day\n\r", ch);
    return;
  }

  if (number(1,101) < ch->skills[SKILL_FIRST_AID].learned) {
    GET_HIT(ch)+= number(1,4)+GET_LEVEL(ch, MONK_LEVEL_IND);
    if(GET_HIT(ch) > GET_MAX_HIT(ch))
       GET_HIT(ch) = GET_MAX_HIT(ch);

    af.duration = 24;
  } else {
    af.duration = 6;
    if (ch->skills[SKILL_FIRST_AID].learned < 95 &&
	ch->skills[SKILL_FIRST_AID].learned > 0) {
      if (number(1,101) > ch->skills[SKILL_FIRST_AID].learned) {
	ch->skills[SKILL_FIRST_AID].learned++;
      }
    }    
  }

  af.type = SKILL_FIRST_AID;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.bitvector = 0;
  affect_to_char(ch, &af);    
  return;  
}


void do_disguise(struct char_data *ch, char *argument, int cmd)
{
  struct affected_type af;
    
  send_to_char("You attempt to disguise yourself\n\r", ch);

  if (affected_by_spell(ch, SKILL_DISGUISE)) {
    send_to_char("You can only do this once per day\n\r", ch);
    return;
  }

  if (number(1,101) < ch->skills[SKILL_DISGUISE].learned) {
    struct char_data *k;

    for (k=character_list; k; k=k->next) {
      if (k->specials.hunting == ch) {
	k->specials.hunting = 0;
      }
      if (number(1,101) < ch->skills[SKILL_DISGUISE].learned) {
	if (Hates(k, ch)) {
	  ZeroHatred(k, ch);
	}
	if (Fears(k, ch)) {
	  ZeroFeared(k, ch);
	}
      }
    }
  } else {
    if (ch->skills[SKILL_DISGUISE].learned < 95 &&
	ch->skills[SKILL_DISGUISE].learned > 0) {
      if (number(1,101) > ch->skills[SKILL_DISGUISE].learned) {
	ch->skills[SKILL_DISGUISE].learned++;
      }
    }    
  }

  af.type = SKILL_DISGUISE;
  af.duration = 24;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.bitvector = 0;
  affect_to_char(ch, &af);    
  return;  
}

/* Skill for climbing walls and the like -DM */
void do_climb( struct char_data *ch, char *arg, int cmd)
{
  extern char *dirs[];
  int dir;
  struct room_direction_data *exitp;
  int was_in, roll;
  extern char *dirs[];

  char buf[256], type[128], direction[128];

  if (GET_MOVE(ch) < 10) {
    send_to_char("You're too tired to do that\n\r", ch);
    return;
  }

  if (MOUNTED(ch)) {
    send_to_char("Yeah... right... while mounted\n\r", ch);
    return;
  }

  /*
    make sure that the argument is a direction, or a keyword.
  */

  for (;*arg == ' '; arg++);

  only_argument(arg,direction);

  if ((dir = search_block(direction, dirs, FALSE)) < 0) {
    send_to_char("You can't climb that way.\n\r", ch);
    return;
  }


  exitp = EXIT(ch, dir);
  if (!exitp) {
    send_to_char("You can't climb that way.\n\r", ch);
    return;
  }

  if(!IS_SET(exitp->exit_info, EX_CLIMB)) {
    send_to_char("You can't climb that way.\n\r", ch);
    return;
  }

  if (dir == UP) {
    if (real_roomp(exitp->to_room)->sector_type == SECT_AIR &&
	!IS_AFFECTED(ch, AFF_FLYING)) {
      send_to_char("You have no way of getting there!\n\r", ch);
      return;
    }
  }

  if (IS_SET(exitp->exit_info, EX_ISDOOR) &&
      IS_SET(exitp->exit_info, EX_CLOSED)) {
    send_to_char("You can't climb that way.\n\r", ch);
    return;
  }

  sprintf(buf, "$n attempts to climb %swards", dirs[dir]);
  act(buf, FALSE, ch, 0, 0, TO_ROOM);
  sprintf(buf, "You attempt to climb %swards\n\r", dirs[dir]);
  send_to_char(buf, ch);

  GET_MOVE(ch) -= 10;

  /*
    now we've checked for failures, time to check for success;
    */
  if (ch->skills) {
    if (ch->skills[SKILL_CLIMB].learned) {
      roll = number(1, 100);
      if (roll > ch->skills[SKILL_CLIMB].learned) {
	slip_in_climb(ch, dir, exitp->to_room);
	LearnFromMistake(ch, SKILL_CLIMB, 0, 95);
      } else {

	  was_in = ch->in_room;

	  char_from_room(ch);
	  char_to_room(ch, exitp->to_room);
	  do_look(ch, "", 0);
	  DisplayMove(ch, dir, was_in, 1);
	  if (!check_falling(ch)) {
	    if (IS_SET(RM_FLAGS(ch->in_room), DEATH) && 
		GetMaxLevel(ch) < LOW_IMMORTAL) {
	      NailThisSucker(ch);
	      return;
	    }

	  }
	  WAIT_STATE(ch, PULSE_VIOLENCE*3);
	  GET_MOVE(ch) -= 10;
	  return;	  
	}
      }
     else {
      send_to_char("You just don't know the nuances of climbing.\n\r", ch);
      slip_in_climb(ch, dir, exitp->to_room);
      return;
    }
  } else {
    send_to_char("You're just a goofy mob.\n\r", ch);
    return;
  }
}


void slip_in_climb(struct char_data *ch, int dir, int room)
{
 int i;

 i = number(1, 6);

 if(dir != DOWN) {
   act("$n falls down and goes splut.", FALSE, ch, 0, 0, TO_ROOM);
   send_to_char("You fall.\n\r", ch);
 }

 else {
   act("$n loses $s grip and falls further down.", FALSE, ch, 0, 0, TO_ROOM);
   send_to_char("You slip and start to fall.\n\r", ch);
   i += number(1, 6);
   char_from_room(ch);
   char_to_room(ch, room);
   do_look(ch, "", 0);
 }

 GET_POS(ch) = POSITION_SITTING;
 if(i > GET_HIT(ch))
   GET_HIT(ch) = 1;
 else
   GET_HIT(ch) -= i;
}