/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: act.obj1.c                                    Part of CircleMUD *
*  Usage: object handling routines -- get/drop and container handling     *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"

#define MARTHA_VNUM 2059
#define DONATE_RNUM 2024

/* extern variables */
extern struct str_app_type str_app[];
extern int top_of_world;
extern struct index_data *obj_index;
extern struct obj_data *obj_proto;
extern struct str_app_type str_app[];
extern struct room_data *world;
extern struct char_data *martha;
extern struct obj_data *don_box;
extern struct dual_list_type nonreg_plurals[];
struct char_data *give_find_vict(struct char_data *ch, char *arg1);
void   clear_timer(struct obj_data *object);
char   *report_cost(int gold);
void   drop_excess_gold(struct char_data *ch, int amount);
int    report_money_weight(int amount);
int    report_pennies(int amount);
int    report_groats(int amount);
int    report_crowns(int amount);
bool   is_plural(char *arg);
char   *make_plural(char *arg);
void	perform_drop_gold(struct char_data *ch, int amount,
			  byte mode, sh_int RDR);
int	perform_drop(struct char_data *ch, struct obj_data *obj,
		     byte mode, char *sname, sh_int RDR);
void   hitch(struct char_data *ch, struct obj_data *cart, struct char_data *vict, char quiet);
void   unhitch(struct char_data *ch, struct obj_data *cart, struct char_data *vict, char quiet);
bool do_get_mob(struct char_data *ch, struct char_data *victim);
void do_drop_mob(struct char_data *ch);
void   perform_sheath(struct char_data *ch, struct obj_data *weapon,
		 struct obj_data *scabbard)
{
  if (GET_ITEM_TYPE(weapon) != ITEM_WEAPON) {
    send_to_char("You can only put weapons in sheaths.\r\n",ch);
    return;
  }
  if (IS_GODITEM(weapon) && !IS_GODITEM(scabbard)) {
    act("$p is too holy to go in $P.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
    return;
  }
  if (scabbard->contains) {
    act("$P seems to have a weapon in it already.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
    return;
  }
  if (scabbard->obj_flags.value[0] > 0) {
    if (scabbard->obj_flags.value[0] != obj_index[weapon->item_number].virtual) {
      act("$p does not belong in $P.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
      return;
    }
  }
  else if (scabbard->obj_flags.value[3] != weapon->obj_flags.value[3]) {
    act("That sort of weapon does not belong in this kind of sheath.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
    return;}

  else if (GET_OBJ_SIZE(scabbard) > GET_OBJ_SIZE(weapon)) {
    act("$p is too big to fit in $P.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
    return;
  }
  
  /*  else if (GET_OBJ_SIZE(scabbard) < GET_OBJ_SIZE(weapon)) {
    act("$p is too small to fit in $P.",
	FALSE, ch, weapon, scabbard, TO_CHAR);
    return;
  }*/
  
  obj_from_char(weapon,0);
  clear_timer(weapon);
  obj_to_obj (weapon, scabbard);
  act("You slip $p into $P.", FALSE, ch, weapon, scabbard, TO_CHAR);
  act("$n slips $p into $P.", FALSE, ch, weapon, scabbard, TO_ROOM);  
}

void   perform_put(struct char_data *ch, struct obj_data *obj,
		 struct obj_data *container)
{
  if (GET_ITEM_TYPE(container) == ITEM_SCABBARD) {
    perform_sheath(ch,obj,container);
    return;
  }
  if (GET_OBJ_WEIGHT(container) + GET_OBJ_WEIGHT(obj) > container->obj_flags.value[0])
    act("$p won't fit in $P.", FALSE, ch, obj, container, TO_CHAR);
  else {
    obj_from_char(obj,0);
    clear_timer(obj);
    obj_to_obj (obj, container);
    act("You put $p in $P.", FALSE, ch, obj, container, TO_CHAR);
    act("$n puts $p in $P.", TRUE, ch, obj, container, TO_ROOM);
  }
}

   
/* The following put modes are supported by the code below:

	1) put <object> <container>
	2) put all.<object> <container>
	3) put all <container>

	<container> must be in inventory or on ground.
	all objects to be put into container must be in inventory.
*/

ACMD(do_put)
{
   char	arg1[MAX_INPUT_LENGTH];
   char	arg2[MAX_INPUT_LENGTH];
   struct obj_data *obj;
   struct obj_data *next_obj;
   struct obj_data *container;
   struct char_data *tmp_char;
   int	obj_dotmode, cont_dotmode;

   argument_interpreter(argument, arg1, arg2);
   obj_dotmode = find_all_dots(arg1);
   cont_dotmode = find_all_dots(arg2);

   if (cont_dotmode != FIND_INDIV)
      send_to_char("You can only put things into one container at a time.\r\n", ch);
   else if (!*arg1)
      send_to_char("Put what in what?\r\n", ch);
   else if (!*arg2) {
      sprintf(buf, "What do you want to put %s in?\r\n",
	      ((obj_dotmode != FIND_INDIV) ? "them" : "it"));
      send_to_char(buf, ch);
   } else {
      generic_find(arg2, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &container);
      if (!container) {
	 sprintf(buf, "You don't see a %s here.\r\n", arg2);
	 send_to_char(buf, ch);
      } else if ((GET_ITEM_TYPE(container) != ITEM_CONTAINER) && 
		 (GET_ITEM_TYPE(container) != ITEM_SCABBARD)) {
	 act("You cannot put things in $p.", FALSE, ch, container, 0, TO_CHAR);
      } else if (IS_SET(container->obj_flags.value[1], CONT_CLOSED)) {
	 send_to_char("You'd better open it first!\r\n", ch);
      } else {
	 if (obj_dotmode == FIND_ALL) {	    /* "put all <container>" case */
	    /* check and make sure the guy has something first */
	    if (container == ch->inventory && !ch->inventory->next_content)
	       send_to_char("You don't seem to have anything to put in it.\r\n", ch);
	    else for (obj = ch->inventory; obj; obj = next_obj) {
	       next_obj = obj->next_content;
	       if (obj != container)
		  perform_put(ch, obj, container);
	    }
	 } else if (obj_dotmode == FIND_ALLDOT) {  /* "put all.x <cont>" case */
	    if (!(obj = get_obj_in_list_vis(ch, arg1, ch->inventory))) {
		if (is_plural(arg1))
		    sprintf(buf, "You don't seem to have any %s.\r\n", arg1);
		else
		    sprintf(buf, "You don't seem to have any %s.\r\n", make_plural(arg1)); 
	       send_to_char(buf, ch);
	    } else while (obj) {
	       next_obj = get_obj_in_list_vis(ch, arg1, obj->next_content);
	       if (obj != container)
		  perform_put(ch, obj, container);
	       obj = next_obj;
	    }
	 } else {		    /* "put <thing> <container>" case */
	    if (!(obj = get_obj_in_list_vis(ch, arg1, ch->inventory))) {
	       sprintf(buf, "You aren't carrying %s %s.\r\n", AN(arg1), arg1);
	       send_to_char(buf, ch);
	    } else if (obj == container)
	       send_to_char("You attempt to fold it into itself, but fail.\r\n", ch);
	    else
	       perform_put(ch, obj, container);
	 }
      }
   }
}



int	can_take_obj(struct char_data *ch, struct obj_data *obj)
{
    if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) {
	if (obj->description){
	sprintf(buf, "%s: You can't carry that many items.\r\n",OBJS(obj, ch));
	send_to_char(buf, ch);}
	return 0;}
    else if (!(CAN_WEAR(obj, ITEM_TAKE))) {
	if (obj->description){
	    sprintf(buf, "%s: You can't take that!\r\n", OBJS(obj, ch));
	    send_to_char(buf, ch);}
	return 0;}
    else if ((IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj)) > CAN_CARRY_W(ch)) {
	if (obj->description){
	    sprintf(buf,"%s: You can't carry that much weight.\r\n",OBJS(obj, ch));
	    send_to_char(buf, ch);}
	return 0;}
    else if (IS_GODITEM(obj) && GET_LEVEL(ch) < LEVEL_BUILDER){
	if (obj->description){
	    sprintf(buf, "%s: You aren't holy enough to use this item.\r\n", OBJS(obj, ch));
	    send_to_char(buf, ch);}
	return 0;			    		     
    }
    
    return 1;
}


void	get_check_money(struct char_data *ch, struct obj_data *obj)
{
    int amount;
    
    amount = obj->obj_flags.value[0];
    if ((GET_ITEM_TYPE(obj) == ITEM_MONEY) && (amount > 0)) {
	obj_from_char(obj,1);
	if (amount > 1 && amount != 1000 && amount != 100 && amount != 10) {
	    sprintf(buf, "There were %s.\r\n", report_cost(amount));
	    send_to_char(buf, ch);
	}
	else if (amount > 1){
	    sprintf(buf, "There was %s.\r\n", report_cost(amount));
	    send_to_char(buf, ch);
	}
	drop_excess_gold(ch, amount);
	extract_obj(obj,0);
    }
}
void	perform_unsheath(struct char_data *ch, struct obj_data *weapon,
				   struct obj_data *scabbard)
{
  int where;

  if (!(where = can_wield(ch, weapon)))
    return;
  obj_from_obj(weapon);
  clear_timer(weapon);
  if (ch->specials.carrying)
    do_drop_mob(ch);      
  act ("You slide $p from $P.",FALSE,ch,weapon, scabbard,TO_CHAR);
  act ("$n slides $p from $P.",FALSE,ch,weapon, scabbard,TO_ROOM);
  if (affected_by_spell(ch, SPELL_SPASMS) && !number(0,1)){
    send_to_char("Your hands are trembling too much.\r\n",ch);
    act("$n tries to wield $p, but $s hands shake.",FALSE,ch,weapon,0,TO_ROOM);
    obj_to_char(weapon, ch, 1);
    perform_drop(ch,weapon,SCMD_DROP,"drop",0);
    return;
  }
  
  equip_char(ch, weapon, where);
  wear_message(ch, weapon, where);
}

void	perform_get_from_container(struct char_data *ch, struct obj_data *obj,
				   struct obj_data *cont, int mode)
{
  if (GET_ITEM_TYPE(cont) == ITEM_SCABBARD) {
    perform_unsheath(ch,obj, cont);
    return;
  }
  if (mode == FIND_OBJ_INV || can_take_obj(ch, obj)) {
    if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch))
      sprintf(buf, "%s: You can't hold any more items.\r\n", OBJS(obj, ch));
    else 
      if (can_take_obj(ch,obj))
	{
	  obj_from_obj(obj);
	  obj_to_char(obj, ch, 0);
	  act("You get $p from $P.", FALSE, ch, obj, cont, TO_CHAR);
	  act("$n gets $p from $P.", TRUE, ch, obj, cont, TO_ROOM);
	  get_check_money(ch, obj);
	}
    
  }
}

void	get_from_container(struct char_data *ch, struct obj_data *cont,
			   char *arg, int mode)
{
  struct obj_data *obj, *next_obj;
  int obj_dotmode, found = 0, to_room;

  obj_dotmode = find_all_dots(arg);
  to_room = real_room(cont->obj_flags.value[3]);
  if (IS_SET(cont->obj_flags.value[1], CONT_CLOSED)){
    act("The $p is closed.", FALSE, ch, cont, 0, TO_CHAR);
    return;
  }
  if (obj_dotmode == FIND_ALL) {
    if (HASROOM(cont))
      {
	for (obj = world[to_room].contents;obj;){
	  next_obj = obj->next_content;
	  if (CAN_SEE_OBJ(ch,obj)){
	    found = 1;
	    if (can_take_obj(ch,obj))
	      {
		GET_OBJ_WEIGHT(cont)
		  = MAX(10, GET_OBJ_WEIGHT(cont)-
			GET_OBJ_WEIGHT(obj));
		act("$N gets $p.",TRUE,0,obj,ch,TO_ROOM); 
		obj_from_room(obj);
		obj_to_char(obj, ch, 0);
		act("$n gets $p from $P.",
		    TRUE,ch,obj,cont,TO_ROOM);
		act("You get $p from $P.",
		    TRUE,ch,obj,cont,TO_CHAR); 
		get_check_money(ch,obj);
	      }
	  }
	  obj = next_obj;
		   
	}
      }
    else
      {
	for (obj = cont->contains; obj; obj = next_obj) {
	  next_obj = obj->next_content;
	  if (CAN_SEE_OBJ(ch, obj)) {
	    found = 1;
	    perform_get_from_container(ch, obj, cont, mode);
	  }
	}
      }
    if (!found)
      act("$p seems to be empty.", FALSE, ch, cont, 0, TO_CHAR);
  } else if (obj_dotmode == FIND_ALLDOT) {
    if (!*arg) {
      send_to_char("Get all of what?\r\n", ch);
      return;
    }
    if (HASROOM(cont))
      {
	obj = get_obj_in_list_vis(ch, arg, world[to_room].contents);
	while(obj) {
	  next_obj = get_obj_in_list_vis(ch, arg, obj->next_content);
	  if (CAN_SEE_OBJ(ch,obj)) {
	    found = 1;
	    if (can_take_obj(ch,obj))
	      {
		GET_OBJ_WEIGHT(cont)
		  = MAX(10,GET_OBJ_WEIGHT(cont)-
			GET_OBJ_WEIGHT(obj));
		act("$N gets $p.",TRUE,0,obj,ch,TO_ROOM);
		obj_from_room(obj);
		obj_to_char(obj, ch, 0);
		act("$n gets $p from $P.",
		    TRUE,ch,obj,cont,TO_ROOM);
		act("You get $p from $P.",
		    TRUE,ch,obj,cont,TO_CHAR);
		get_check_money(ch,obj);
	      }
	  }
	  obj = next_obj;
	}
      }
    else
      {
	obj = get_obj_in_list_vis(ch, arg, cont->contains);
	while (obj) {
	  next_obj = get_obj_in_list_vis(ch, arg, obj->next_content);
	  if (CAN_SEE_OBJ(ch, obj)) {
	    found = 1;
	    perform_get_from_container(ch, obj, cont, mode);
	  }
	  obj = next_obj;
	}
      }
    if (!found) {
      if (is_plural(arg))
	sprintf(buf, "You can't find any %s in $p.", arg);
      else
	sprintf(buf, "You can't find any %s in $p.", make_plural(arg));
      act(buf, FALSE, ch, cont, 0, TO_CHAR);
    }
  }
  else
    {
      if (HASROOM(cont)){
	if (!(obj = get_obj_in_list_vis(ch, arg, world[to_room].contents)))
	  {
	    sprintf(buf, "There doesn't seem to be %s %s in $p.",
		    AN(arg), arg);
	    act(buf, FALSE, ch, cont, 0, TO_CHAR);
	  }
	else
	  if (can_take_obj(ch,obj))
	    {
	      GET_OBJ_WEIGHT(cont) = MAX(10,GET_OBJ_WEIGHT(cont)-
					 GET_OBJ_WEIGHT(obj));
	      act("$N gets $p.",TRUE,0,obj,ch,TO_ROOM);
	      obj_from_room(obj);
	      obj_to_char(obj, ch, 0);
	      act("$n gets $p from $P.",TRUE,ch,obj,cont,TO_ROOM);
	      act("You get $p from $P.",TRUE,ch,obj,cont,TO_CHAR);
	      get_check_money(ch,obj);
	    }		
      }
      else
	{
	  if (!(obj = get_obj_in_list_vis(ch, arg, cont->contains))) {
	    sprintf(buf, "There doesn't seem to be %s %s in $p.",
		    AN(arg), arg);
	    act(buf, FALSE, ch, cont, 0, TO_CHAR);
	  }
	  else
	    perform_get_from_container(ch, obj, cont, mode);
	}
    }
}


int	perform_get_from_room(struct char_data *ch, struct obj_data *obj)
{
   if (can_take_obj(ch, obj)) {
      obj_from_room(obj);
      obj_to_char(obj, ch, 0);
      act("You get $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n gets $p.", TRUE, ch, obj, 0, TO_ROOM);
      get_check_money(ch, obj);
      return 1;
   }

   return 0;
}


void get_from_room(struct char_data *ch, char *arg)
{
   struct obj_data *obj, *next_obj;
   struct char_data *pobj;
   int	dotmode, found = 0;

   dotmode = find_all_dots(arg);

   if (dotmode == FIND_ALL) {
      for (obj = world[ch->in_room].contents; obj; obj = next_obj) {
	 next_obj = obj->next_content;
	 if (CAN_SEE_OBJ(ch, obj) && obj->description) {
	    found = 1;
	    perform_get_from_room(ch, obj);
	 }
      }
      if (!found)
	 send_to_char("There doesn't seem to be anything here.\r\n", ch);
   } else if (dotmode == FIND_ALLDOT) {
      if (!*arg) {
	 send_to_char("Get all of what?\r\n", ch);
	 return;
      }
      if (!(obj = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents))) {
	  if (is_plural(arg))
	      sprintf(buf, "You don't see any %s here.\r\n", arg);
	  else
	      sprintf(buf, "You don't see any %s here.\r\n", make_plural(arg));
	 send_to_char(buf, ch);
      } else while (obj) {
	 next_obj = get_obj_in_list_vis(ch, arg, obj->next_content);
	 perform_get_from_room(ch, obj);
	 obj = next_obj;
      }
   } else {
      if (!(obj = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents))) {
	if (generic_find(arg, FIND_CHAR_ROOM, ch, &pobj, &obj))
	  if (CAN_SEE(ch, pobj)) {
	    do_get_mob(ch, pobj);
	    return;
	  }
	sprintf(buf, "You don't see %s %s here.\r\n", AN(arg), arg); 
	send_to_char(buf, ch);
      }
      else
	perform_get_from_room(ch, obj);
   }
}



ACMD(do_get)
{
   char	arg1[MAX_INPUT_LENGTH];
   char	arg2[MAX_INPUT_LENGTH];

   int	cont_dotmode, found = 0, mode;
   struct obj_data *cont, *next_cont;
   struct char_data *tmp_char;

   if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) 
      send_to_char("Your arms are already full!\r\n", ch);
   else {
      argument_interpreter(argument, arg1, arg2);
      if (!*arg1)
         send_to_char("Get what?\r\n", ch);
      else {
         if (!*arg2)
            get_from_room(ch, arg1);
         else {
	    cont_dotmode = find_all_dots(arg2);
	    if (cont_dotmode == FIND_ALL) { /* use all in inv. and on ground */
	       for(cont = ch->inventory; cont; cont = cont->next_content)
		  if ((GET_ITEM_TYPE(cont) == ITEM_CONTAINER) ||
		       (GET_ITEM_TYPE(cont) == ITEM_SCABBARD)) {
		     found = 1;
		     get_from_container(ch, cont, arg1, FIND_OBJ_INV);
		  }
	       for(cont = world[ch->in_room].contents; cont; cont = cont->next_content)
		  if (CAN_SEE_OBJ(ch, cont) && ((GET_ITEM_TYPE(cont) == ITEM_CONTAINER) || (GET_ITEM_TYPE(cont) == ITEM_SCABBARD))) {
		     found = 1;
		     get_from_container(ch, cont, arg1, FIND_OBJ_ROOM);
		  }
	       if (!found)
		  send_to_char("You can't seem to find any containers.\r\n", ch);
	    } else if (cont_dotmode == FIND_ALLDOT) {
	       if (!*arg2) {
		  send_to_char("Get from all of what?\r\n", ch);
		  return;
	       }
	       cont = get_obj_in_list_vis(ch, arg2, ch->inventory);
	       while (cont) {
		  found = 1;
		  next_cont = get_obj_in_list_vis(ch, arg2, cont->next_content);
		  if ((GET_ITEM_TYPE(cont) != ITEM_CONTAINER) &&
		      (GET_ITEM_TYPE(cont) != ITEM_SCABBARD))
		     act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
		  else
		     get_from_container(ch, cont, arg1, FIND_OBJ_INV);
		  cont = next_cont;
	       }
	       cont = get_obj_in_list_vis(ch, arg2, world[ch->in_room].contents);
	       while (cont) {
		  found = 1;
		  next_cont = get_obj_in_list_vis(ch, arg2, cont->next_content);
		  if ((GET_ITEM_TYPE(cont) != ITEM_CONTAINER) &&
		      (GET_ITEM_TYPE(cont) != ITEM_SCABBARD))
		     act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
		  else
		      get_from_container(ch, cont, arg1, FIND_OBJ_ROOM);
		  cont = next_cont;
	       }
	       if (!found) {
		   if (is_plural(arg2))
		       sprintf(buf, "You can't seem to find any %s here.\r\n", arg2);
		   else
		       sprintf(buf, "You can't seem to find any %s here.\r\n",make_plural(arg2));
		  send_to_char(buf, ch);
	       }
	    } else { /* get <items> <container> (no all or all.x) */
	       mode = generic_find(arg2, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &cont);
	       if (!cont) {
		  sprintf(buf, "You don't have %s %s.\r\n", AN(arg2), arg2);
		  send_to_char(buf, ch);
	       }
	       else if ((GET_ITEM_TYPE(cont) != ITEM_CONTAINER) &&
			(GET_ITEM_TYPE(cont) != ITEM_SCABBARD))
		 act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
	       else
		 get_from_container(ch, cont, arg1, mode);
	    }
	 }
      }
   }
}


void	perform_drop_gold(struct char_data *ch, int amount,
			  byte mode, sh_int RDR)
{
   struct obj_data *obj;

   if (amount <= 0)
      send_to_char("Heh heh heh.. we are jolly funny today, eh?\r\n", ch);
   else if (GET_GOLD(ch) < amount)
      send_to_char("You don't have that many coins!\r\n", ch);
   else {
      if (mode != SCMD_JUNK) {
	 obj = create_money(amount);
	 if (mode == SCMD_DONATE) {
	    send_to_char("You throw some coins into the air where it disappears in a puff of smoke!\r\n", ch);
	    act("$n throws some coins into the air where it disappears in a puff of smoke!", FALSE, ch, 0, 0, TO_ROOM);
	    send_to_room("Some money suddenly appears in mid-air, then drops to the ground!\r\n", RDR, FALSE);
	    obj_to_obj(obj, don_box);
	 } else {
	    send_to_char("You drop some money.\r\n", ch);
	    act("$n drops some money.", FALSE, ch, 0, 0, TO_ROOM);
	    obj_to_room(obj, ch->in_room, FALSE);
	 }
      } else {
	 act("$n drops some money which disappears in a puff of smoke!", FALSE, ch, 0, 0, TO_ROOM);
	 send_to_char("You drop some money which disappears in a puff of smoke!\r\n", ch);
      }
      change_gold(ch, -amount);
   }
}


#define VANISH(mode) ((mode == SCMD_DONATE || mode == SCMD_JUNK) ? \
		      "  It vanishes in a puff of smoke!" : "")

int	perform_drop(struct char_data *ch, struct obj_data *obj,
		     byte mode, char *sname, sh_int RDR)
{
   extern sh_int donation_room_1;


   int	value=0;

   if (IS_SET(obj->obj_flags.extra_flags, ITEM_NODROP)) {
      sprintf(buf, "You can't %s %s, it must be CURSED!\r\n", sname, OBJS(obj, ch));
      send_to_char(buf, ch);
      return 0;
   }

   if ((mode == SCMD_DONATE) && IS_SET(obj->obj_flags.extra_flags, ITEM_NODONATE)) {
      sprintf(buf, "You can't donate %s.\r\n", OBJS(obj, ch));
      send_to_char(buf, ch);
      return 0;
   }

   sprintf(buf, "You %s %s.%s\r\n", sname, OBJS(obj, ch), VANISH(mode));
   send_to_char(buf, ch);
   sprintf(buf, "$n %ss $p.%s", sname, VANISH(mode));
   act(buf, TRUE, ch, obj, 0, TO_ROOM);

   obj_from_char(obj,0);

   switch(mode) {
   case SCMD_DROP:
     if (ch->specials.carrying)
       do_drop_mob(ch);
     obj_to_room(obj, ch->in_room, FALSE);
     return 0;
     break;
   case SCMD_DONATE:
     if(RDR == real_room(donation_room_1)){
       obj_to_char(obj,martha, 0);
       send_to_room("Martha grabs something from thin air!\r\n", RDR, FALSE);
     }
     return 0;
     break;
   case SCMD_JUNK:
     if ((GET_ITEM_TYPE(obj) == ITEM_CONTAINER) &&
	 (obj->obj_flags.value[3] < 0))
       value = MAX(10, MIN(200,obj->obj_flags.cost/4));
     
     extract_obj(obj,0);
     return value;
     break;
   default:
     logg("SYSERR: Incorrect argument passed to perform_drop");
     break;
   }
   return 0;
}



ACMD(do_drop)
{
   extern sh_int donation_room_1;
   extern sh_int donation_room_2;
   extern sh_int donation_room_3;
   struct obj_data *obj, *next_obj;
   sh_int RDR = 0;
   byte	mode = SCMD_DROP;
   int	dotmode, amount = 0;
   char	*sname;

   switch (subcmd) {
   case SCMD_JUNK:
      sname = "junk";
      mode = SCMD_JUNK;
      break;
   case SCMD_DONATE:
      sname = "donate";
      mode = SCMD_DONATE;
      switch (number(0, 5)) {
      case 0:
      case 1: RDR = real_room(donation_room_1); break;
      }
      if (RDR == NOWHERE) {
	 send_to_char("Sorry, you can't donate anything right now.\r\n", ch);
	 return;
      }
      break;
   default:
      sname = "drop";
      break;
   }

   argument = one_argument(argument, arg);

   if (!*arg) {
      sprintf(buf, "What do you want to %s?\r\n", sname);
      send_to_char(buf, ch);
      return;
   } else if (is_number(arg)) {
      amount = atoi(arg);
      argument = one_argument(argument, arg);
      if (!str_cmp("coins", arg) || !str_cmp("coin", arg) || !str_cmp("pennies", arg) || !str_cmp("penny",arg)){
	  if (amount > report_pennies(GET_GOLD(ch)))
	      send_to_char("You don't have that many pennies.\r\n",ch);
	  else
	      perform_drop_gold(ch, amount*10, mode, RDR);}
      else if (!str_cmp("groats", arg) || !str_cmp("groat", arg) ){
	  if (amount > report_groats(GET_GOLD(ch)))
	      send_to_char("You don't have that many groats.\r\n",ch);
	  else
	      perform_drop_gold(ch, amount*100, mode, RDR);}
      else if (!str_cmp("crowns", arg) || !str_cmp("crown", arg) ){
	  if (amount > report_crowns(GET_GOLD(ch)))
	      send_to_char("You don't have that many crowns.\r\n",ch);
	  else
	      perform_drop_gold(ch, amount*1000, mode, RDR);}
      else {
	  /* code to drop multiple items.  anyone want to write it? -je */
	  send_to_char("Sorry, you can't do that (yet)...\r\n", ch);
      }
      return;
   } else {
      dotmode = find_all_dots(arg);

      /* Can't junk or donate all */
      if ((dotmode == FIND_ALL) && (subcmd == SCMD_JUNK || subcmd == SCMD_DONATE)) {
         if (subcmd == SCMD_JUNK) 
	    send_to_char("Go to the dump if you want to junk EVERYTHING!\r\n", ch);
         else 
	    send_to_char("Go do the donation room if you want to donate EVERYTHING!\r\n", ch);
         return;
      }

      if (dotmode == FIND_ALL) {
	if (ch->specials.carrying)
	  do_drop_mob(ch);
	if (!ch->inventory)
	  send_to_char("You don't seem to be carrying anything.\r\n", ch);
	 else
	   for (obj = ch->inventory; obj; obj = next_obj) {
	     next_obj = obj->next_content;
	     amount += perform_drop(ch, obj, mode, sname, RDR);
	   }
      } else if (dotmode == FIND_ALLDOT) {
	 if (!*arg) {
	    sprintf(buf, "What do you want to %s all of?\r\n", sname);
	    send_to_char(buf, ch);
	    return;
	 }
	 
         if (!(obj = get_obj_in_list_vis(ch, arg, ch->inventory))) {
	     if (is_plural(arg))
		 sprintf(buf, "You don't seem to have any %s.\r\n", arg);
	     else
		 sprintf(buf, "You don't seem to have any %s.\r\n", make_plural(arg));
	    send_to_char(buf, ch);
	 }
	if (ch->specials.carrying)
	  do_drop_mob(ch);
	while (obj) {
	    next_obj = get_obj_in_list_vis(ch, arg, obj->next_content);
	    amount += perform_drop(ch, obj, mode, sname, RDR);
	    obj = next_obj;
	 }
      } else {
	if (ch->specials.carrying && isname(arg, ch->specials.carrying->player.name))
	  do_drop_mob(ch);
	else if (!(obj = get_obj_in_list_vis(ch, arg, ch->inventory))) {
	    sprintf(buf, "You don't seem to have %s %s.\r\n", AN(arg), arg);
	    send_to_char(buf, ch);
	 } else
	    amount += perform_drop(ch, obj, mode, sname, RDR);
      }
   }

   if (amount && (subcmd == SCMD_JUNK) && (GET_LEVEL(ch)< 10) &&
       ((GET_GOLD(ch) + GET_BANK_GOLD(ch)) < 1000)) {
     send_to_char("You have been rewarded by the gods!\r\n", ch);
     act("$n has been rewarded by the gods!", TRUE, ch, 0, 0, TO_ROOM);
     change_gold(ch , amount*2);
     
   }
}


void	perform_give(struct char_data *ch, struct char_data *vict,
		     struct obj_data *obj)
{
    if (IS_GODITEM(obj) && GET_LEVEL(vict) < LEVEL_BUILDER){
      sprintf(buf, "%s: You aren't holy enough to use this item.\r\n", OBJS(obj, vict));
      send_to_char(buf, vict);
      sprintf(buf, "%s isn't holy enough to use this item.\r\n", GET_NAME(vict));
      send_to_char(buf, ch);      
      return;}	
    
   if (IS_SET(obj->obj_flags.extra_flags, ITEM_NODROP)) {
      act("You can't let go of $p!!  Yeech!", FALSE, ch, obj, 0, TO_CHAR);
      return;
   }

   if (IS_CARRYING_N(vict) >= CAN_CARRY_N(vict)) {
      act("$N seems to have $S hands full.", FALSE, ch, 0, vict, TO_CHAR);
      return;
   }

   if (GET_OBJ_WEIGHT(obj) + IS_CARRYING_W(vict) > CAN_CARRY_W(vict)) {
      act("$E can't carry that much weight.", FALSE, ch, 0, vict, TO_CHAR);
      return;
   }

   obj_from_char(obj,0);
   obj_to_char(obj, vict,0);
   act("You give $p to $N.", FALSE, ch, obj, vict, TO_CHAR);
   act("$n gives you $p.", FALSE, ch, obj, vict, TO_VICT);
   act("$n gives $p to $N.", TRUE, ch, obj, vict, TO_NOTVICT);
}

/* utility function for give */
struct char_data *give_find_vict(struct char_data *ch, char *arg1)
{
   struct char_data *vict;
   char arg2[MAX_INPUT_LENGTH];

   strcpy(buf, arg1);
   argument_interpreter(buf, arg1, arg2);
   if (!*arg1) {
      send_to_char("Give what to who?\r\n", ch);
      return 0;
   } else if (!*arg2) {
      send_to_char("To who?\r\n", ch);
      return 0;
   } else if (!(vict = get_char_room_vis(ch, arg2))) {
      send_to_char("No-one by that name here.\r\n", ch);
      return 0;
   } else if (vict == ch) {
      send_to_char("What's the point of that?\r\n", ch);
      return 0;
   } else
      return vict;
}


void	perform_give_gold(struct char_data *ch, struct char_data *vict,
			  int amount)
{
    if (amount <= 0) {
	send_to_char("Heh heh heh ... we are jolly funny today, eh?\r\n", ch);
	return;
    }

    if ((GET_GOLD(ch) < amount) && (IS_NPC(ch) || (GET_LEVEL(ch) < LEVEL_GOD))) {
	send_to_char("You haven't got that many coins!\r\n", ch);
	return;
    }

    send_to_char("Ok.\r\n", ch);
    sprintf(buf, "%s gives you %s.\r\n", PERS(ch, vict), report_cost(amount));
    send_to_char(buf, vict);
    act("$n gives some money to $N.", TRUE, ch, 0, vict, TO_NOTVICT);

    drop_excess_gold(vict, amount);
    if (IS_NPC(ch) || (GET_LEVEL(ch) < LEVEL_GOD))
      change_gold(ch, -amount);

    
}

ACMD(do_unhitch)
{
    ACMD(do_say);
    struct char_data *vict;
    struct obj_data *cart;
    
    one_argument(argument, arg);
    if (!*arg){
	send_to_char("Unhitch what?\r\n", ch);
	return;
    }
    if (!(vict = get_char_room_vis(ch, arg))){
	send_to_char("Unhitch who?\r\n", ch);       
	return;}
    
    else if (!(cart = IS_PULLING(vict)))
	{
	    act("$N isn't hitched to anything.\n",TRUE,ch,0,vict,TO_CHAR);
	    return;
	}
    if ((vict->master != ch && vict != ch)
	    && vict->in_room == ch->in_room
	    && (GET_LEVEL(ch) < LEVEL_BUILDER)){
	    if (IS_MOB(vict)){
		if (CAN_SPEAK(vict)){
		    act("$n slaps your wrist.",TRUE,vict,0,ch,TO_VICT);
		    act("$n slaps $N's wrist.",TRUE,vict,0,ch,TO_ROOM);
		    do_say(vict,"Hey get your own cart!",0,0);}
		else{
		    act("$n shies away from $N.",TRUE,vict,0,ch,TO_NOTVICT);
		    act("$n shies away from you.",TRUE,vict,0,ch,TO_VICT);
		}
	    }
	    else
		act("$n just tried to remove your cart.",TRUE,ch,0,vict,TO_VICT);
	    return;
	    
    }
    else
	unhitch(ch, cart, vict, 0);
    return;
    
}

void unhitch(struct char_data *ch, struct obj_data *cart, struct char_data *vict, char quiet)
{
    struct follow_type *j,*k;
    if (!quiet){
	act("You unhitch $N.",FALSE,ch,0,vict,TO_CHAR);
	act("$n unhitches $N from $p.",FALSE,ch,cart,vict,TO_ROOM);
	act("$n unhitches $p from you.",FALSE,ch,cart,vict,TO_VICT);
    }
    if (cart->pulled_by->follower == vict){
	k = cart->pulled_by;
	cart->pulled_by = k->next;
	free(k);}
    else {
	for (k = cart->pulled_by; k->next->follower != vict;k = k->next)
	    ;
	j = k->next;
	k->next = j->next;
	free(j);
    }
    vict->specials.cart = 0;
	
}

void hitch(struct char_data *ch, struct obj_data *cart, struct char_data *vict, char quiet)
{
    struct follow_type *k;
    
    CREATE(k, struct follow_type, 1);

    k->follower = vict;
    k->next = cart->pulled_by;
    cart->pulled_by = k;
    vict->specials.cart = cart;
    if (!quiet){
	act("You hitch $p to $N",TRUE,ch,cart,vict,TO_CHAR);
	act("$n hitches $p to $N",TRUE,ch,cart,vict,TO_ROOM);
	act("$n hitches $p to you",TRUE,ch,cart,vict,TO_VICT);	       
    }
}
ACMD(do_mount)
{
  ACMD(do_say);
  struct char_data *victim;
  int num, result;

  one_argument(argument, arg);
  if (!*arg) {
    send_to_char("Mount what?\r\n",ch);
    return;
  }
  if (ch->specials.mount) {
    send_to_char("You should dismount before you try mounting another mount.\r\n",ch);
    return;
  }     
  victim = get_char_room_vis(ch, arg);
  if (victim) {
    
    if (victim == ch) {
      send_to_char("You attempt to clamber on your own back - to no avail.\r\n"
		   ,ch);
      return;
    }
    if (victim->specials.fighting) {
      send_to_char("You can't mount beasts in combat.\r\n",ch);
      return;
    }
    if (IS_PULLING(victim)) {
      send_to_char("You can mount beasts pulling carts.\r\n",ch);
      return;
    }
    if (victim->specials.carried_by) {
      act("$N is being carried you cannot mount $M."
	  ,FALSE,ch,0,victim,TO_CHAR);
      return;
    }
    if (ch->specials.carrying) {
      act("You had better drop $N first.",
	  FALSE,ch,0,ch->specials.carrying,TO_CHAR);
      return;
    }
    if (victim->specials.rider) {
      act("$N already has a rider.",FALSE,ch,0,victim,TO_CHAR);
      return;
    }
    if ((GET_SIZE(ch) > GET_SIZE(victim)) &&
	((GET_SIZE(ch) - GET_SIZE(victim)) > 1)) {
      act("You can't ride $N, $E's too big.",FALSE,ch,0,victim,TO_CHAR);
      return;
    }
     else if ((GET_SIZE(ch) < GET_SIZE(victim)) &&
	((GET_SIZE(victim) - GET_SIZE(ch)) > 1)) {
      act("You can't ride $N, $E's too small.",FALSE,ch,0,victim,TO_CHAR);
      return;
    }
    if ((GET_WEIGHT(ch) + IS_CARRYING_W(ch)) > CAN_CARRY_W(victim)) {
      act("You can't ride $N. You are too heavy.",FALSE,ch,0,victim,TO_CHAR); 
      return;
    }      
    if (GET_SKILL(ch, SKILL_MOUNT) < (num = number(0,31))) {
      act("$n trys to mount up on $N and falls on $s backside.",
	  FALSE,ch,0,victim,TO_ROOM);
      act("$n trys to mount up on you and falls on $s backside.",
	  FALSE,ch,0,victim,TO_VICT);
      act("You try to mount up on $N and fall on your backside.",
	  FALSE,ch,0,victim,TO_CHAR);
      GET_POS(ch) = POSITION_SITTING;
      if (num == 0 && (number(0,31) == 0)
	  && (number(0,31) > GET_SKILL(ch, SKILL_MOUNT))
	  && (GET_SKILL(ch, SKILL_MOUNT) < 30)) {
	send_to_char("You realise how you can improve your mounting technique\r\n.",ch);
	SET_SKILL(ch, SKILL_MOUNT,(GET_SKILL(ch,SKILL_MOUNT) +1));
	rmdamage(ch,GET_HEIGHT(victim)/number(5,20));
      }
      return;
    }
    if (IS_MOB(victim) && MOB_FLAGGED(victim, MOB_DOCILE) 
	&& !IS_AFFECTED(victim, AFF_CHARM))
	{
	  if (victim->master && victim->master != ch) 
	    stop_follower_quiet(victim);
	  if (!victim->master)
	    if (!circle_follow(victim, ch))
	      if ((result = add_follower(victim,ch)) == 0){
		act("$N tried to follow you but you already have too many followers."
		    ,FALSE,ch,0,victim,TO_CHAR);
		act("$n already has too many followers."
		    ,FALSE,ch,0,victim,TO_VICT);
		return;
	      }
	}
    if (victim->master != ch) {
      send_to_char("Your mount must follow you first.\r\n",ch);
      if (IS_MOB(victim)) {
	act("$n attempts to mount $N.",FALSE,ch,0,victim,TO_ROOM);
	if (!CAN_SPEAK(victim))
	  act("$N shies away from $n.",FALSE,ch,0,victim,TO_ROOM);
	else {
	  act("$N tells $n to get lost.",FALSE,ch,0,victim,TO_ROOM);
	  act("$N tells you to get lost.",FALSE,ch,0,victim,TO_CHAR);
	}
      }
      else
	act("$N just tried to mount up on your back.",
	    FALSE,victim,0,ch,TO_CHAR);
      return;
    }

    act("$n mounts up on $N's back.",FALSE,ch,0,victim,TO_NOTVICT);
    act("$n mounts up on your back.",FALSE,ch,0,victim,TO_VICT);
    act("You mount up on $N's back.",FALSE,ch,0,victim,TO_CHAR);

    if (IS_AFFECTED(ch, AFF_SNEAK))
      affect_from_char(ch, SKILL_SNEAK);
    if (IS_SET(MOB_FLAGS(victim), MOB_MEMORY))
      REMOVE_BIT(MOB_FLAGS(victim), MOB_MEMORY);
    ch->specials.mount = victim;
    victim->specials.rider = ch;
    IS_CARRYING_W(victim) += (GET_WEIGHT(ch) + IS_CARRYING_W(ch));
    return;
  }
  sprintf(buf,"You don't see %s here.\r\n",arg);
  send_to_char(buf,ch);
  return;
}

bool do_get_mob(struct char_data *ch, struct char_data *victim)
{
  ACMD(do_say);
  int num, result;

  if (ch->specials.carrying) {
    act("You are already carrying $N.",
	FALSE,ch,0,ch->specials.carrying, TO_CHAR);
    return(FALSE);
  }     
  if (victim) {
    if (victim == ch) {
      send_to_char("Don't be silly.\r\n",ch);
      return(FALSE);
    }
    if (victim->specials.fighting) {
      send_to_char("You pick people up in combat.\r\n",ch);
      return(FALSE);
    }
    if (IS_PULLING(victim)) {
      send_to_char("Try unhitching them first.\r\n",ch);
      return(FALSE);
    }
    if (victim->specials.rider) {
      act("Get $N to dismount first.",
	  FALSE,ch,0,victim->specials.rider,TO_CHAR);
      return(FALSE);
    }
    if (victim->specials.mount) {
      act("Get $N to dismount first.",FALSE,ch,0,victim,TO_CHAR);
      return(FALSE);
    }
    if (victim->specials.carried_by) {
      act("$N is already being carried.",FALSE,ch,0,victim,TO_CHAR);
      return(FALSE);
    }
    if (ch->equipment[WIELD] || ch->equipment[HOLD]) {
      send_to_char("Your hands seem to be full.\r\n",ch);
      return;
    }
    if ((GET_SIZE(ch) > GET_SIZE(victim)) &&
	     ((GET_SIZE(ch) - GET_SIZE(victim)) > 1)) {
      act("You can't carry $N, $E's too big.",FALSE,ch,0,victim,TO_CHAR);
      return(FALSE);
    }
    if ((GET_WEIGHT(victim) + IS_CARRYING_W(victim)) > CAN_CARRY_W(ch)) {
      act("You can't carry $N, $E's too heavy.",FALSE,ch,0,victim,TO_CHAR); 
      return(FALSE);
    }      
    if (IS_MOB(victim) && MOB_FLAGGED(victim, MOB_DOCILE) 
	&& !IS_AFFECTED(victim, AFF_CHARM))
	{
	  if (victim->master && victim->master != ch) 
	    stop_follower_quiet(victim);
	  if (!victim->master)
	    if (!circle_follow(victim, ch))
	      if ((result = add_follower(victim,ch)) == 0){
		act("$N tried to follow you but you already have too many followers."
		    ,FALSE,ch,0,victim,TO_CHAR);
		act("$n already has too many followers."
		    ,FALSE,ch,0,victim,TO_VICT);
		return;
	      }
	}
    if (victim->master != ch) {
      act("$N must follow you first.",FALSE,ch,0,victim, TO_CHAR);
      if (IS_MOB(victim)) {
	act("$n attempts to pick up $N.",FALSE,ch,0,victim,TO_ROOM);
	if (!CAN_SPEAK(victim))
	  act("$N shies away from $n.",FALSE,ch,0,victim,TO_ROOM);
	else {
	  act("$N tells $n to get lost.",FALSE,ch,0,victim,TO_ROOM);
	  act("$N tells you to get lost.",FALSE,ch,0,victim,TO_CHAR);
	}
      }
      else
	act("$N just tried to pick you up.",
	    FALSE,victim,0,ch,TO_CHAR);
      return(FALSE);
    }

    act("$n picks up $N.",FALSE,ch,0,victim,TO_NOTVICT);
    act("$n picks you up.",FALSE,ch,0,victim,TO_VICT);
    act("You pick $N up.",FALSE,ch,0,victim,TO_CHAR);

    if (IS_AFFECTED(ch, AFF_SNEAK))
      affect_from_char(ch, SKILL_SNEAK);
    if (IS_SET(MOB_FLAGS(victim), MOB_MEMORY))
      REMOVE_BIT(MOB_FLAGS(victim), MOB_MEMORY);
    ch->specials.carrying = victim;
    victim->specials.carried_by = ch;
    IS_CARRYING_W(ch) += (GET_WEIGHT(victim) + IS_CARRYING_W(victim));
    return(TRUE);
  }
  send_to_char("Get who?\r\n",ch);
  return(FALSE);
}
void do_drop_mob(struct char_data *ch)
{
  struct char_data *victim;

  if (victim = ch->specials.carrying) {
    victim->specials.carried_by = 0;
    ch->specials.carrying = 0;
    IS_CARRYING_W(ch) -= (GET_WEIGHT(victim)  + IS_CARRYING_W(victim));
    act("You drop $N.",FALSE,ch,0,victim, TO_CHAR);
    act("$n drops You.",FALSE,ch,0,victim, TO_VICT);    
    act("$n drops $N.",FALSE,ch,0,victim, TO_NOTVICT);    
  }
}
void unmount(struct char_data *ch) {
  struct char_data *victim;
  
  ch->specials.mount->specials.rider = 0;
  victim = ch->specials.mount;
  ch->specials.mount = 0;
  IS_CARRYING_W(victim) -= (GET_WEIGHT(ch) + IS_CARRYING_W(ch));
  
}

ACMD(do_dismount)
{
  int num;

  if (!ch->specials.mount) {
    send_to_char("Dismount?  You are not even riding anything.\r\n",ch);
      return;
    }
    if (GET_SKILL(ch, SKILL_MOUNT) < (num = number(0,31))) {
      act("$n trys to dismount $N and falls on $s backside.",
	  FALSE,ch,0,ch->specials.mount,TO_ROOM);
      act("$n trys to dismount you and falls on $s backside.",
	  FALSE,ch,0,ch->specials.mount,TO_VICT);
      act("You try to dismount $N and fall on your backside.",
	  FALSE,ch,0,ch->specials.mount,TO_CHAR);
      unmount(ch);
      GET_POS(ch) = POSITION_SITTING;
      if (num == 0 && (number(0,31) == 0)
	  && (number(0,31) > GET_SKILL(ch, SKILL_MOUNT))
	  && (GET_SKILL(ch, SKILL_MOUNT) < 30)) {
	send_to_char("You realise how you can improve your dismounting technique\r\n.",ch);
	SET_SKILL(ch, SKILL_MOUNT,(GET_SKILL(ch,SKILL_MOUNT) + 1));
	rmdamage(ch,GET_HEIGHT(ch->specials.mount)/number(5,20));
      }
    }
    else {
      act("$n climbs off $N's back.",FALSE,ch,0,ch->specials.mount,TO_NOTVICT);
      act("$n climbs off your back.",FALSE,ch,0,ch->specials.mount,TO_VICT);
      act("You climb off $N's back.",FALSE,ch,0,ch->specials.mount,TO_CHAR);
      unmount(ch);
    }

}



ACMD(do_hitch)
{
    ACMD(do_say);
    struct char_data *vict;
    struct obj_data *cart;
    
    argument_interpreter(argument, arg, buf2);
    if (!*arg){
	send_to_char("Hitch what?\r\n", ch);
	return;
    }
    else if (!(cart = get_obj_in_list_vis(ch,arg,world[ch->in_room].contents)))
	{
		   if (is_plural(arg))
		       sprintf(buf, "You can't seem to find any %s here.\r\n", arg);
		   else
		       sprintf(buf, "You can't seem to find any %s here.\r\n", make_plural(arg));

	    send_to_char(buf, ch);
	    return;}
    if (!(vict = get_char_room_vis(ch, buf2))){
	send_to_char("Hitch to what?\r\n", ch);       
	return;}
    if (vict == ch){
	send_to_char("Now why would you want to do that?\r\n", ch);       
	return;}
    if (vict->specials.rider) {
      send_to_char("You can hitch carts to mounts.\r\n",ch);
      return;
    }
    if(!IS_CART(cart)){
	send_to_char("You can't hitch that!\r\n",ch);
	return;}
    if (cart && vict){
	if (IS_PULLING(vict))
	    {
		do_unhitch(ch,GET_NAME(vict),0,0);
		if (!IS_PULLING(vict)) 
		    return;
	    }
	if (vict->master != ch && vict != ch
	    && (GET_LEVEL(ch) < LEVEL_BUILDER)){	       
	    if (IS_MOB(vict)){
		if (CAN_SPEAK(vict)){
		    act("$n slaps your wrist.",TRUE,vict,0,ch,TO_VICT);
		    act("$n slaps $N's wrist.",TRUE,vict,0,ch,TO_NOTVICT);
		    do_say(vict,"Hey watch it!",0,0);}
		else {
		    act("$n shies away from $N.",TRUE,vict,0,ch,TO_NOTVICT);
		       act("$n shies away from you.",TRUE,vict,0,ch,TO_VICT);
		}
	    }
	    else
		   act("$n just tried to hitch a cart to you.",TRUE,ch,0,vict,TO_VICT);
	    return;
	}
	if(IS_NPC(vict) && !IS_SET(vict->specials2.act, MOB_SENTINEL))
	    SET_BIT(vict->specials2.act, MOB_SENTINEL);
	hitch(ch,cart,vict,0);
    }
    return;
}
ACMD(do_give)
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg3[MAX_INPUT_LENGTH];
    int	amount, dotmode;
    struct char_data *vict;
    struct obj_data *obj, *next_obj;

    half_chop(argument, arg1, arg2);
    
    *arg3 = LOWER(*arg2);
    if (!*arg1)
	send_to_char("Give what to whom?\r\n", ch);
    else if (is_number(arg1)) {
	amount = atoi(arg1);
	if (!(vict = give_find_vict(ch, arg2)))
	    return;
	if (!str_cmp("coins", arg2) || !str_cmp("coin", arg2) || !str_cmp("pennies", arg2) || !str_cmp("penny",arg2))
	    perform_give_gold(ch,vict, amount*10);
	else if (!str_cmp("groats", arg2) || !str_cmp("groat", arg2) )
	    perform_give_gold(ch,vict, amount*100);
	else if (!str_cmp("crowns", arg2) || !str_cmp("crown", arg2) )
	    perform_give_gold(ch,vict, amount*1000);
	else {
	    /* code to give multiple items.  anyone want to write it? -je */
	    send_to_char("Sorry, you can't do that (yet)...\r\n", ch);
	    return;
	}
   }
    else {
       if (!(vict = give_find_vict(ch, argument)))
	   return;
       if (IS_NPC(vict) && GET_LEVEL(ch) <= LEVEL_BUILDER){
	   act("$N refuses your kind offer.",FALSE,ch,0,vict,TO_CHAR);
	   return;}
      dotmode = find_all_dots(argument);
       if (dotmode == FIND_ALL) {
	   if (!ch->inventory)
	       send_to_char("You don't seem to be holding anything.\r\n", ch);
	   else for (obj = ch->inventory; obj; obj = next_obj) {
	       next_obj = obj->next_content;
	       perform_give(ch, vict, obj);
	   }
       } else if (dotmode == FIND_ALLDOT) {
	   if (!*argument) {
	       send_to_char("All of what?\r\n", ch);
	       return;
	   }
	 if (!(obj = get_obj_in_list_vis(ch, argument, ch->inventory))) {
	     if(is_plural(argument))
		 sprintf(buf, "You don't seem to have any %s.\r\n", argument);
	     else
		 sprintf(buf, "You don't seem to have any %s.\r\n", make_plural(argument));
	     send_to_char(buf, ch);
	 } else while (obj) {
	     next_obj = get_obj_in_list_vis(ch, argument, obj->next_content);
	     perform_give(ch, vict, obj);
	    obj = next_obj;
	 }
       } else {
	   if (!(obj = get_obj_in_list_vis(ch, argument, ch->inventory))) {
	       sprintf(buf, "You don't seem to have %s %s.\r\n", AN(argument), argument);
	       send_to_char(buf, ch);
	   } else
	       perform_give(ch, vict, obj);
       }
   }
}

bool   is_plural(char *arg)
{
    char *loc;
    int i;
/* this is a really simple minded algorithm -
   checks to see if last char is an 's'
   AJN 29th Nov 94 */
    loc = arg;
    if (!*arg)
	return(FALSE);
/* check exceptions */
   for(i=0;strcmp(nonreg_plurals[i].singular,"\n");i++)
	{
	    if (!strcmp(nonreg_plurals[i].plural,arg)) /*already plural*/
		return(TRUE);
	    else if (!strcmp(nonreg_plurals[i].singular,arg))
		return(FALSE);
	}
    
    while (*loc != '\0')
	loc++;
    loc--;
    if (*loc == 's')
	return(TRUE);
    
    return(FALSE);	
}