/
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

ranger.c				Ranger skills.  Some of which are
					based on ranger code from RoI,
					Realms of Imagination.  All code
					in this file has basically been
					rewritten/debugged though, but we
					still extend credit where it's due.

		******** 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 ***

 - Began MAJOR ranger revamp, modification of existing skill/spellset and
   additional skills/spells.  Proposal submitted by Cyng Swiftwind, ranger
   enthusiast.  3/27/98 -jtrhone
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

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

// external vars
extern struct dex_skill_type dex_app_skill[];
extern char *dirs[];
extern char *comm_dirs[];

// external function prototypes
void name_from_drinkcon(obdata *obj);
void name_to_drinkcon(obdata *obj, int type);
void weight_change_object(obdata *obj, int weight);
void perform_whirlwind(chdata *ch);
void perform_cyclone(chdata *ch);

// enhanced whirlwind  3/27/98 -jtrhone
ACMD(do_cyclone)
{
   int percent, prob;

   one_argument(argument, arg);

   if (!IS_RANGER(ch) || !GET_SKILL(ch, SKILL_CYCLONE))
   {
     send_to_char("Though you have heard such a skill exists, actually performing "
 		  "it is beyond your abilities.\n\r",ch);
     return;
   }

   if (GET_MOVE(ch) <= 25)
   {
       send_to_char("You feel too tired to do that.\n\r", ch);
       return;
   }

   if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
   {
     send_to_char("Some strange force counteracts your violent actions.\n\r",ch);
     return;
   }

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

   if (percent <= prob) 
   {
       GET_MOVE(ch) -= 25;
       ch->pc_specials->cyclone = 2;

       if (!ch->specials.fighting)
	   perform_cyclone(ch);
       WAIT_STATE(ch, PULSE_VIOLENCE * 5);
   }
   else
   {
       GET_MOVE(ch) -= 10;
       send_to_char("You stumble out of control!\n\r",ch);
       WAIT_STATE(ch, PULSE_VIOLENCE * 3);
   }
}
// let rangers meditate to up regen rate...  3/27/98 -jtrhone
ACMD(do_meditate)
{
  struct affected_type af;

  if (IN_NOWHERE(ch)) return;

  if (!IS_RANGER(ch) || !GET_SKILL(ch, SKILL_MEDITATE))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
		 "it is beyond your abilities.\n\r",ch);
    return;
  }

  if (!ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("There is too much violence surrounding you for meditation.\n\r",ch);
    return;
  }

  if (GET_MANA(ch) < 40)
  {
    send_to_char("You feel too weak to do such, Monshai.\n\r",ch);
    return;
  }
  GET_MANA(ch) -= 40;

  if (GET_SKILL(ch, SKILL_MEDITATE) < number(1, 101))
  {
    act("You lose your concentration.", FALSE, ch, 0, 0, TO_CHAR);
    return;
  }

  act("$n lowers $s head in concentration.", TRUE, ch, 0, 0, TO_ROOM);

  if (!affected_by_spell(ch, SKILL_MEDITATE))
  {
    af.type      = SKILL_MEDITATE;
    af.duration  = GET_LEVEL(ch)/2;
    af.modifier  = 0;
    af.location  = APPLY_NONE;
    af.bitvector = 0;
    af.bitvector2 = 0;

    affect_to_char(ch, &af);
    act("You concentrate on regeneration!", FALSE, ch, 0, 0, TO_CHAR);
    act("$n slips into a state of meditation.", FALSE, ch, 0, 0, TO_ROOM);
  }
  else
    act("You're already in that state of meditation!", FALSE, ch, 0, 0, TO_CHAR);
}

// let rangers create a marker object to aid in travel 3/27/98 -jtrhone
ACMD(do_trailblaze)
{
  obdata *obj;
  char *argu = argument;
  int dir;

  if (IN_NOWHERE(ch)) return;

  if (!IS_RANGER(ch) || !GET_SKILL(ch, SKILL_TRAILBLAZE))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
		 "it is beyond your abilities.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Which direction do you wish to mark?\n\r",ch);
    return;
  } 
  
  if ((dir = search_block(argu, comm_dirs, FALSE)) == -1 || dir >= NUM_OF_DIRS)
  {
    send_to_char("Valid dirs: N, S, E, W, NE, NW, SE, SW, U, D.\n\r",ch);
    return;
  }

  if (!EXIT(ch, dir))
  {
    send_to_char("That direction does not exist...\n\r",ch);
    return;
  }

  if (!(obj = read_object(190, VIRTUAL)))
  {
    act("There was an error, notify an immortal (obj vnum 190).",FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  // make the unique descrips and timer
  sprintf(buf, "Someone has made markings here.  The markings point %s.",dirs[dir]);
  obj->description = STR_DUP(buf); 
  obj->timer = GET_LEVEL(ch) / 10;

  obj_to_room(obj, ch->in_room);

  act("You create $p.", FALSE,ch,obj,0,TO_CHAR);
  act("$n has created $p.",TRUE,ch,obj,0,TO_ROOM);
}

// let rangers create a torch, obj vnum 3030  3/27/98 -jtrhone
ACMD(do_create_torch)
{
  obdata *obj;

  if (IN_NOWHERE(ch)) return;

  if (!IS_RANGER(ch) || !GET_SKILL(ch, SKILL_CREATE_TORCH))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
		 "it is beyond your abilities.\n\r",ch);
    return;
  }

  if (GET_MOVE(ch) < 5)
  {
    act("You feel too weak to do such...",FALSE, ch, 0,0,TO_CHAR);
    return;
  }
   
  if (FIGHTING(ch))
  {
    act("You cannot do such while fighting.",FALSE, ch, 0,0,TO_CHAR);
    return;
  }
  
  if (!(obj = read_object(3030, VIRTUAL)))
  {
    act("There was an error, notify an immortal (obj vnum 3030).",FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  // set torch duration to level / 2
  LIGHT_TIME(obj) = GET_LEVEL(ch)/2;

  GET_MOVE(ch) -= 5;

  obj_to_char(obj, ch);

  act("You create $p.", FALSE,ch,obj,0,TO_CHAR);
  act("$n has created $p.",TRUE,ch,obj,0,TO_ROOM);
}

ACMD(do_notrack)
{
  struct room_affect_type af;

  if ((!IS_RANGER(ch) && !IS_SHAMAN(ch)) || !GET_SKILL(ch, SKILL_NOTRACK))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
		 "it is beyond your abilities.\n\r",ch);
    return;
  }

  if (!WILDERNESS(world[ch->in_room].terrain_type)) {
    send_to_char("You can only perform such in the wilderness.\n\r", ch);
    return;
  }

  if (spell_affects_room(ch->in_room, SKILL_NOTRACK))
  {
    act("This area is already affected by notrack.",FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_SKILL(ch, SKILL_NOTRACK) < number(1, 101))
  {
    act("You fail to prevent tracking through this area...",FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  if (GET_MOVE(ch) < 30)
  {
    act("You feel too weak to do such...",FALSE, ch, 0,0,TO_CHAR);
    return;
  }

  GET_MOVE(ch) -= 30;

  af.spell = SKILL_NOTRACK;
  af.caster = ch;
  af.duration  = GET_LEVEL(ch);
  af.bitvector = 0;
  affect_to_room(ch->in_room, &af);

  act("You prevent tracking through this area.", FALSE, ch, 0, 0, TO_CHAR);
  act("$n concentrates on the surrounding area.",TRUE,ch,0,0,TO_ROOM);
}

ACMD(do_trek)
{
  struct affected_type af;      
  char *argu = argument;
  int amount;

  if (IS_NPC(ch) || !IS_RANGER(ch))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
		 "it is beyond your abilities.\n\r",ch);
    return;
  }

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

  if (!GET_SKILL(ch, SKILL_TREK))
  {
    send_to_char("Though you have heard such a skill exists, actually performing "
                 "it is beyond your abilities.\n\r",ch);
    return;
  }

  if (!WILDERNESS(world[ch->in_room].terrain_type)) {
    send_to_char("You fail to concentrate enough in this area.\n\r", ch);
    return;
  }

  if (!is_number(argu) || (amount = atoi(argu)) <= 0)
  {
    send_to_char("Transfer amount must be a positive integer.\n\r",ch);
    return;
  }

  if (amount > GET_HIT(ch) - 5)
  {
    send_to_char("That would weaken you too much.\n\r",ch);
    return;
  }

  if (GET_SKILL(ch, SKILL_TREK) < number(1, 101))
  {
    amount = number(1,GET_HIT(ch) - 5 );
    act("Something went wrong!",FALSE,ch,0,0,TO_CHAR); 
  }
   
  GET_HIT(ch) -= amount;
  GET_MOVE(ch) += amount;
  act("You fortify your body's endurance.",FALSE,ch,0,0,TO_CHAR);
  act("$n concentrates on $mself.",TRUE,ch,0,0,TO_ROOM);

  affect_from_char(ch, SKILL_TREK);

  af.type      = SKILL_TREK;
  af.duration  = GET_INT(ch)/2;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = 0;
  af.bitvector2 = 0;
  affect_to_char(ch, &af);
}

ACMD(do_presence)
{
  struct affected_type af;      

  if (!ch->desc) return;

  if (!IS_RANGER(ch))
  {
    send_to_char("You do not posess that ability.\n\r",ch);
    return;
  }

  if (!GET_SKILL(ch, SKILL_PRESENCE))
  {
    send_to_char("You do not know of that skill yet, ranger.\n\r",ch);
    return;
  }

  if (INVALID_ROOM(ch->in_room))
  {
    send_to_char("You mind spins.  Invalid room.  Seek Immortal help.\n\r",ch);
    return;
  }

  if (!WILDERNESS(world[ch->in_room].terrain_type) || ROOM_FLAGGED(ch->in_room, NO_MAGIC)) 
  {
    send_to_char("You fail to establish a presence here.\n\r", ch);
    return;
  }

  if (GET_SKILL(ch, SKILL_PRESENCE) < number(1, 110))
  {
    act("You fail to establish a presence here.",FALSE,ch,0,0,TO_CHAR);
    act("$n fails to establish a presence here.",TRUE,ch,0,0,TO_ROOM);
    return;
  }

  if (GET_MANA(ch) < 50)
  {
    act("You feel too mentally weak to establish a presence here.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  GET_MANA(ch) -= 50;

  affect_from_char(ch, SKILL_PRESENCE);

  act("You establish a mental presence in this area!",FALSE,ch,0,0,TO_CHAR);
  act("$n's presence seems to dominate the area!.",TRUE,ch,0,0,TO_ROOM);

  af.type      = SKILL_PRESENCE;
  af.duration  = GET_INT(ch)/6;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = 0;
  af.bitvector2 = AFF2_PRESENCE;
  affect_to_char(ch, &af);
  add_room_snooper(&world[ch->in_room], ch->desc); 
}

ACMD(do_become)
{
  chdata *vict;
  char *argu = argument;
  struct affected_type af;      

  if (IS_NPC(ch)) return;

  if (!IS_RANGER(ch))
  {
    send_to_char("You do not posess that ability.\n\r",ch);
    return;
  }

  if (!GET_SKILL(ch, SKILL_BECOME))
  {
    send_to_char("You do not know of that skill yet, ranger.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Meld spirits with whom?\n\r",ch);
    return;
  }  

  if (!(vict = get_char_room_vis(ch, argu)))
  {
    send_to_char("Meld spirits with whom?\n\r",ch);
    return;
  }

  if (IS_PC(vict))
  {
    send_to_char("You may not meld spirits with other players.\n\r",ch);
    return;
  }
  
  if (vict->desc || CHAR_FLAGGED(vict, CH_BECAME))
  {
    act("$N is already posessed by somebody!",FALSE,ch,0,vict,TO_CHAR);
    return;
  }
 
  if (!MOB_CLASS_FLAGGED(vict, MOB_MAMMAL | MOB_AQUATIC | MOB_AVIAN))
  {
    act("You cannot meld spirits with $N.",FALSE,ch,0,vict,TO_CHAR);
    return;
  }

  if (GET_LEVEL(ch) < GET_LEVEL(vict)*2)
  {
    act("$N is too powerful for you to meld with.",TRUE,ch,0,vict,TO_CHAR);
    return;
  }

  if (GET_MANA(ch) < GET_LEVEL(vict)*2)
  {
    act("You are too mentally drained to meld spirits with $N.",TRUE,ch,0,vict,TO_CHAR);
    return;
  }

  GET_MANA(ch) -= GET_LEVEL(vict)*2;

  if (GET_SKILL(ch, SKILL_BECOME) < number(1, 101))
  {
    act("You fail to meld spirits with $N!",TRUE,ch,0,vict,TO_CHAR);
    return;
  }

  act("Your spirit melds with $N!",FALSE,ch,0,vict,TO_CHAR);
  act("$n's spirit jumps from $m into $N!.",TRUE,ch,0,vict,TO_ROOM);

  af.type      = SKILL_BECOME;
  af.duration  = GET_INT(ch)/6;
  af.modifier  = 0;
  af.location  = APPLY_NONE;
  af.bitvector = 0;
  af.bitvector2 = AFF2_SWITCH;

  affect_to_char(vict, &af);
  SET_BIT(CHAR_FLAGS(vict), CH_BECAME);
 
  ch->desc->character = vict;
  ch->desc->original = ch;
  vict->desc = ch->desc;
  ch->desc = NULL;
}

ACMD(do_call_wildlife)
{
  chdata *vict;
  int mobnum;

  if (!IS_RANGER(ch))
  {
    send_to_char("Nothing heeds your call for assistance.\n\r",ch);
    return;
  }

  // Added 08/22/98 -callahan
  if (ZONE_FLAGGED(world[InRoom(ch)].zone, Z_ARENA)) {
    send_to_char("Nothing heeds your call for assistance.\n\r", ch);
    return;
  }

  if (ROOM_FLAGGED(ch->in_room, NO_MOB))
  {
    send_to_char("Nothing heeds your call for assistance.\n\r", ch);
    return;
  }

  if (!WILDERNESS(world[ch->in_room].terrain_type)) {
    send_to_char("Nothing heeds your call for assistance.\n\r", ch);
    return;
  }

  if (GET_MOVE(ch) < GET_LEVEL(ch))
  {
    send_to_char("You feel too tired to call for assistance.\n\r",ch);
    return;
  }

  for (mobnum = 0, vict = character_list; vict; vict = vict->next)
    if (SUMMONER(vict) == GET_IDNUM(ch))
      mobnum += GET_LEVEL(vict);
  if (mobnum >= (2 * GET_LEVEL(ch)))
  {
    send_to_char("You have reached your maximum calling capacity.\n\r",ch);
    return;
  }

  GET_MOVE(ch) -= GET_LEVEL(ch);
  if (GET_SKILL(ch, SKILL_CALL_WILDLIFE) < number(1, 101))
  {
    send_to_char("You call for assistance, but nothing heeds your call.\n\r",ch);
    return;
  }

  // HOPEFULLY, system zone 1, mobs exist 100 - 110 -roa
  mobnum = 103 + (GET_LEVEL(ch) / 10);
  mobnum = number(100, mobnum);
  if (!(vict = read_mobile(mobnum, VIRTUAL)))
  {
    send_to_char("Seek immortal assistance, wildlife non-existence error.\n\r",ch);
    return;
  }
  SET_BIT(CHAR_FLAGS(vict), CH_SUMMONED);
  SUMMONER(vict) = GET_IDNUM(ch);
  char_to_room(vict, ch->in_room);
  act("You call for assistance, and $N heeds your call.",TRUE,ch,0,vict,TO_CHAR);
  act("$n calls for assistance and $N heeds the call.",TRUE,ch,0,vict,TO_ROOM);
  add_follower(vict, ch);
}

ACMD(do_forage) 
{
  rmdata *room_temp;
  obdata *tmp_obj; 
  int percent, prob, r_num;

  if (IN_NOWHERE(ch) || IS_NPC(ch)) return;

  if (!IS_DRUID(ch) && !IS_RANGER(ch) && !IS_SHAMAN(ch)) {
    send_to_char("You fail to forage anything useful.\n\r", ch);
    return;
  }

  if (GET_MOVE(ch) <= 5) {
    send_to_char("You don't have enough energy to do that right now.\n\r", ch);
    return;
  }

  room_temp = &world[ch->in_room];
  if (!WILDERNESS(room_temp->terrain_type)) {
    send_to_char("You can only forage in the wilderness.\n\r", ch);
    return;
  }

  GET_MOVE(ch) -= 5;

  percent = number(1, 110);
  prob = GET_SKILL(ch, SKILL_FORAGE);
  if (percent > prob) {
    send_to_char("You fail to find any food here.\n\r", ch);
    return;
  }

  r_num = number(140, 149);
  tmp_obj = read_object(r_num, VIRTUAL);
  
  if (tmp_obj) 
  {
    tmp_obj->value[0] = 1 + GET_LEVEL(ch);
    obj_to_char(tmp_obj, ch);     
    act("$n forages $p.", FALSE, ch, tmp_obj, 0, TO_ROOM);
    act("You forage $p.", FALSE, ch, tmp_obj, 0, TO_CHAR);
  }
  else
  {
    send_to_char("Something went wrong.  Seek immortal help..\n\r",ch);
    return;
  }
}

ACMD(do_heal_wounds) 
{
  chdata *victim;
  int healpoints;

  one_argument(argument, arg);

  if (GET_MOVE(ch) < 15) {
    send_to_char("You don't have enough energy to do that right now.\n\r", ch);
    return;
  }
  GET_MOVE(ch) -= 15;

  if (!(victim = get_char_room_vis(ch, arg))) 
    victim = ch;

  if (number(1, 120) > GET_SKILL(ch, SKILL_HEALWOUNDS)) {
    send_to_char("You fail to cure the wounds.\n\r", ch);
    return;
  }

  healpoints = dice(2, 8) + (int)((float)GET_LEVEL(ch) * .25);
  if ( (healpoints + GET_HIT(victim)) > GET_MAX_HIT(victim))
    GET_HIT(victim) = GET_MAX_HIT(victim);
  else
    GET_HIT(victim) += healpoints;
  
  update_pos(victim);
  
  affect_from_char(victim, SPELL_POISON);
  affect_from_char(victim, SPELL_POISON_DART);
  
  if (ch != victim)
  {
    act("$n applies a smelly poultice to $N's wounds.", 
        FALSE, ch, 0, victim, TO_ROOM);
    act("You apply a smelly poultice to $N's wounds.", 
        FALSE, ch, 0, victim, TO_CHAR);
  }
  else
  {
    act("$n applies a smelly poultice to $s wounds.", FALSE, ch, 0, 0, TO_ROOM);
    act("You apply a smelly poultice to your wounds.", FALSE, ch, 0, 0, TO_CHAR);
  }
  act("Your wounds are healed.", FALSE, victim, 0, 0, TO_CHAR);
}

ACMD(do_divine)
{
  rmdata *room_temp;
  obdata *obj;
  int percent, prob;
  int water;

  if (IN_NOWHERE(ch)) return;

  if (!IS_DRUID(ch) && !IS_RANGER(ch)) {
    send_to_char("You don't know how do that.\n\r", ch);
    return;
  }

  if (GET_MANA(ch) < 5) {
    send_to_char("You don't have enough energy to do that right now.\n\r", ch);
    return;
  }

  room_temp = &world[ch->in_room];
  if (!WILDERNESS(room_temp->terrain_type)) {
    send_to_char("You can only search for water in the wilderness.\n\r", ch);
    return;
  }

  GET_MANA(ch) -= 5;

  one_argument(argument, arg);

  if (!*arg)
  {
    send_to_char("Usage: divine <liquid container in inventory>.\n\r",ch);
    return;
  }

  if(!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) {
    send_to_char("You don't have that item.\n\r", ch);
    return;
  }

  percent = number(1, 110);
  prob = GET_SKILL(ch, SKILL_DIVINE);
  if (percent > prob) 
  {
    send_to_char("You couldn't find a stream.\n\r", ch);
    return;
  }

  // basically create water spell from here...
  if (ITEM_TYPE(obj) == ITEM_DRINKCON) 
  {
    if ((obj->value[2] == LIQ_WATER) && (obj->value[1] != 0)) 
    {
      name_from_drinkcon(obj);
      obj->value[2] = LIQ_SLIME;
      name_to_drinkcon(obj, LIQ_SLIME);
    } 
    else 
    {
      water = 2 * GET_LEVEL(ch);
      
      water = MIN(obj->value[0] - obj->value[1], water);
      if (water) 
      {
	obj->value[2] = LIQ_WATER;
	obj->value[1] = water;
	
	weight_change_object(obj, water);
	
	name_from_drinkcon(obj);
	name_to_drinkcon(obj, LIQ_WATER);
	act("You fill $p from a hidden stream.", FALSE, ch, obj, 0, TO_CHAR);
        return;
      }
    }
  }
  act("You couldn't find a stream to fill $p.", FALSE, ch, obj, 0,TO_CHAR);
}

ACMD(do_skin)
{
  obdata *tmp_obj;
  obdata *tmp_corpse;
  obdata *obj, *temp;
  int r_num;
  int percent, prob;

  if (IN_NOWHERE(ch)) return;

  if (!IS_RANGER(ch)) {
    send_to_char("You don't know how do that.\n\r", ch);
    return;
  }

  one_argument(argument, arg);  

  if (!*arg)
  {
    send_to_char("Skin what?\n\r",ch);
    return;
  }

  if (!(tmp_corpse = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents))) 
  {
    send_to_char("You can't find a corpse to skin.\n\r", ch);
    return;
  }

  if (!IS_CORPSE(tmp_corpse))
  {
    send_to_char("That's not a corpse!\n\r",ch);
    return;
  }

  if (GET_MOVE(ch) < 15) {
    send_to_char("You don't have enough energy to do that right now.\n\r", ch);
    return;
  }    

  GET_MOVE(ch) -= 5;
  
  percent = number(1, 110);
  prob = GET_SKILL(ch, SKILL_SKIN);
  if (percent > prob) {
    send_to_char("This corpse's skin is too tough for you.\n\r", ch);
    return;
  }

  if (tmp_corpse->plr_num) {
    send_to_char("You may not skin a corpse of a player.\n\r", ch);
    return;
  }

  act("You expertly skin a piece of armor from the corpse.", 
      FALSE, ch, 0, 0, TO_CHAR);
  act("$n expertly skins a piece of armor from the corpse.", 
      FALSE, ch, 0, 0, TO_ROOM);
  
  GET_MOVE(ch) -= 10;
  GET_MOVE(ch) = MAX(GET_MOVE(ch), 0);

  r_num = 110;
  tmp_obj = read_object(r_num, VIRTUAL);
  
  if (tmp_obj) 
  {
    tmp_obj->value[0] = 2 + (int)((float)GET_LEVEL(ch)*.10);
    float_sink_object(tmp_obj, ch->in_room); 
    
    obj = tmp_corpse->contains;
    while (obj) 
    {
      temp = obj->next_content;
      obj_from_obj(obj);
      float_sink_object(obj, ch->in_room);
      obj = temp;
    }

    tmp_corpse->contains = 0;
    extract_obj(tmp_corpse);
  }
  else
  {
    send_to_char("Something went wrong.  Seek immortal help..\n\r",ch);
    return;
  }
}  

ACMD(do_filet)
{
  obdata *tmp_obj;
  int prob, percent, r_num;
  obdata *corpse;
  obdata *obj, *temp;
  
  if (IN_NOWHERE(ch)) return;

  if (!IS_DRUID(ch) && !IS_RANGER(ch)) {
    send_to_char("You don't know how do that.\n\r", ch);
    return;
  }

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

  one_argument(argument, arg);
      
  if (!*arg)
  {
    send_to_char("Filet what?\n\r",ch);
    return;
  }

  if (!(corpse = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents))) {
    send_to_char("You don't see that here.\n\t", ch);
    return;
  }

  if (!IS_CORPSE(corpse))
  {
    send_to_char("That's not a corpse!\n\r",ch);
    return;
  }

  if (corpse->plr_num) 
  {
    send_to_char("You may not filet the corpse of a player.\n\r", ch);
    return;
  }

  if (GET_MOVE(ch) <= 5) 
  {
    send_to_char("You don't have enough energy to do that right now.\n\r", ch);
    return;
  }
  GET_MOVE(ch) -=5;

  percent=number(1, 110);
  prob = GET_SKILL(ch, SKILL_FILET);
  if (percent>prob) {
    send_to_char("You failed to filet the corpse.\n\r", ch);
    return;
  }

  act("You carve a big juicy steak from $p.", FALSE, ch, corpse, 0, TO_CHAR);
  act("$n carves a big juicy steak from $p.", FALSE, ch, corpse, 0, TO_ROOM);
  
  r_num = 103;
  tmp_obj = read_object(r_num, VIRTUAL);
  
  if (tmp_obj) {
    tmp_obj->value[0] = 2 + (int)((float)GET_LEVEL(ch)*.10);
    float_sink_object(tmp_obj, ch->in_room); 
    
    obj = corpse->contains;
    while (obj) {
      temp = obj->next_content;
      obj_from_obj(obj);
      float_sink_object(obj, ch->in_room);
      obj = temp;
    }

  extract_obj(corpse);
  }    
  else
  {
    send_to_char("Something went wrong.  Seek immortal help..\n\r",ch);
    return;
  }
}

ACMD(do_befriend)
{
  struct follow_type *f;
  struct affected_type af;
  chdata *victim;
  int charmed_levels;
  int prob, percent;

  void add_follower(chdata *ch, chdata *leader);
  BOOL circle_follow(chdata *ch, chdata *victim);
 
  if (!IS_RANGER(ch)) {
    send_to_char("You don't know how do that.\n\r", ch);
    return;
  }
 
  one_argument(argument, arg);

  if (!(victim = get_char_room_vis(ch, arg))) {
    send_to_char("Whom do you wish to befriend?\n\r", ch);
    return;
  }
  
  if (!IS_NPC(victim)) {
    send_to_char("You can't befriend a player.\n\r", ch);
    return;
  }

  if (victim == ch) {
    send_to_char("You can't befriend yourself.\n\r", ch);
    return;
  }

  if (GET_MANA(ch) <= 10) 
  {
    send_to_char("You don't have enough mental energy to do that right now.\n\r", ch);
    return;
  }

  GET_MANA(ch) -= 10;

  percent = number(1,110);
  prob = GET_SKILL(ch, SKILL_BEFRIEND);
  if (percent > prob) {
    sprintf(buf, "You failed to befriend %s.\n\r",GET_NAME(victim));
    send_to_char(buf, ch);
    return;
  }

  if (GET_LEVEL(victim) > GET_LEVEL(ch))
  {
    act("$N is too powerful for you to befriend.", FALSE, ch, 0, victim, TO_CHAR);
    return;   
  }

 if (MOB_FLAGGED(victim, MOB_NO_CHARM) ||
     SPC_FLAGGED(victim, SPC_NOKILL) ||
     IS_AFFECTED(victim, AFF_SANCTUARY) ||
     saves_spell(victim, SPELL_CHARM_PERSON))
 {
    act("$N resists your efforts.", FALSE, ch, 0, victim, TO_CHAR);
    return; 
 }

  for (charmed_levels=0,f=ch->followers; f; f=f->next)
    if (IS_AFFECTED(f->follower, AFF_CHARM))
      charmed_levels += GET_LEVEL(f->follower);

  if ((charmed_levels + GET_LEVEL(victim)) > GET_LEVEL(ch)) {
    send_to_char("You are unable to befriend that many victims.\n\r",ch);
    return;
  }

  if (!IS_AFFECTED(victim, AFF_CHARM) && !IS_AFFECTED(ch, AFF_CHARM) &&
      (GET_LEVEL(ch) >= GET_LEVEL(victim))) {
    if(circle_follow(victim, ch)) {
      send_to_char("Sorry, following in circles is not allowed.\n\r", ch);
      return;
    }
      
    if (victim->master)
      stop_follower(victim); 

    add_follower(victim, ch);

    af.type = SPELL_CHARM_PERSON;
    if (GET_INT(victim))
      af.duration = (24 * 18) / GET_INT(victim);
    else
      af.duration = 24 * 18;

    af.modifier = 0;
    af.location = 0;
    af.bitvector = AFF_CHARM;
    af.bitvector2 = 0;
    affect_to_char(victim, &af);

    act("$n befriends $N.", TRUE, ch, 0, victim, TO_ROOM);
    act("You befriend $N.", FALSE, ch, 0, victim, TO_CHAR);
  }
}

ACMD(do_camouflage)
{
   byte percent;

   if (IN_NOWHERE(ch)) return;
 
   // Added check for Druids. 08/12/98 -callahan
   if (!IS_RANGER(ch) || !IS_DRUID(ch))
   {
     send_to_char("Leave that skill to those more able.\n\r",ch);
     return;
   }
   
   if (!WILDERNESS(world[ch->in_room].terrain_type))
   {
     send_to_char("You must be in the wilderness.\n\r",ch);
     return;
   }

   if (GET_MOVE(ch) <= 10)
   {
     send_to_char("You feel too tired to try to blend into your surroundings.\n\r",ch);
     return;
   }

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

   GET_MOVE(ch) -= 10;

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

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

   SET_BIT(ch->specials.affected_by, AFF_HIDE);
}

// updated a bit for revamp  3/27/98 -jtrhone
ACMD(do_whirlwind)
{
   int percent, prob;

   one_argument(argument, arg);

   if ((!IS_RANGER(ch) && !IS_MONK(ch)) || !GET_SKILL(ch, SKILL_WHIRLWIND))
   {
     send_to_char("Though you have heard such a skill exists, actually performing "
 		  "it is beyond your abilities.\n\r",ch);
     return;
   }

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

   if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
   {
     send_to_char("Some strange force counteracts your violent actions.\n\r",ch);
     return;
   }

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

   if (percent <= prob) 
   {
       GET_MOVE(ch) -= 10;
       ch->pc_specials->whirlwind = 2;

       if (!ch->specials.fighting)
	   perform_whirlwind(ch);
       WAIT_STATE(ch, PULSE_VIOLENCE * 5);
   }
   else
   {
       GET_MOVE(ch) -= 4;
       send_to_char("You stumble out of control!\n\r",ch);
       WAIT_STATE(ch, PULSE_VIOLENCE * 3);
   }
}

// removed cost from silentwalk  3/27/98 -jtrhone
ACMD(do_silentwalk)
{
   struct affected_type af;

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

   if (IS_AFFECTED(ch, AFF_SNEAK))
      affect_from_char(ch, SKILL_SNEAK);

   if (!IS_RANGER(ch) || !GET_SKILL(ch, SKILL_SILENTWALK)) 
   {
     send_to_char("You fail to walk silently.\n\r",ch);
     return;
   }

   if (number(1, 101) > (GET_SKILL(ch, SKILL_SILENTWALK) + dex_app_skill[GET_DEX(ch)].sneak))
   {
     send_to_char("You fail to walk silently.\n\r",ch);
     return;
   }

   af.type = SKILL_SNEAK;
   af.duration = GET_LEVEL(ch);
   af.modifier = 0;
   af.location = APPLY_NONE;
   af.bitvector = AFF_SNEAK;
   af.bitvector2 = 0;
   affect_to_char(ch, &af);
}

ACMD(do_sweep)
{
   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 (GET_LEVEL(ch) < LEV_AIMP)
  if (ROOM_FLAGGED(ch->in_room, PEACEFUL))
  {
    send_to_char("Your offensive actions lead to naught in this peaceful area.\n\r",ch);
    return;
  }

   if (!IS_RANGER(ch)) 
   {
      send_to_char("Leave that skill to those more able.\n\r", ch);
      return;
   }

   one_argument(argument, arg);

   if (!(victim = get_char_room_vis(ch, arg))) 
   {
      if (FIGHTING(ch))
	 victim = FIGHTING(ch);
      else 
      {
	 send_to_char("Sweep who?\n\r", ch);
	 return;
      }
   }

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

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

   percent = ((10 - (GET_AC(victim) / 10)) << 1) + number(1, 101); 
   prob = GET_SKILL(ch, SKILL_SWEEP);

   if (percent > prob) 
      damage(ch, victim, 0, SKILL_SWEEP, TRUE);
   else
   {
      if (damage(ch, victim, GET_LEVEL(ch) >> 1, SKILL_SWEEP, FALSE) == CHAR_OK)
        VWAIT(victim) += 1;

      if (!EQ(ch, W_FEET) && GET_HIT(ch) > 1)
      {
        send_to_char("Your feet are bare! Ouch!\n\r",ch);
        GET_HIT(ch)--;
      }
      else
        damage_object(EQ(ch, W_FEET), ch);
   }
   WAIT_STATE(ch, PULSE_VIOLENCE * 3);
}