circlemud_squared_0.5.153/cnf/
circlemud_squared_0.5.153/etc/
circlemud_squared_0.5.153/etc/etc/
circlemud_squared_0.5.153/etc/house/
circlemud_squared_0.5.153/etc/misc/
circlemud_squared_0.5.153/etc/plralias/A-E/
circlemud_squared_0.5.153/etc/plralias/F-J/
circlemud_squared_0.5.153/etc/plralias/K-O/
circlemud_squared_0.5.153/etc/plralias/P-T/
circlemud_squared_0.5.153/etc/plralias/U-Z/
circlemud_squared_0.5.153/etc/plralias/ZZZ/
circlemud_squared_0.5.153/etc/plrobjs/
circlemud_squared_0.5.153/etc/plrobjs/A-E/
circlemud_squared_0.5.153/etc/plrobjs/F-J/
circlemud_squared_0.5.153/etc/plrobjs/K-O/
circlemud_squared_0.5.153/etc/plrobjs/P-T/
circlemud_squared_0.5.153/etc/plrobjs/U-Z/
circlemud_squared_0.5.153/etc/plrobjs/ZZZ/
circlemud_squared_0.5.153/etc/text/
circlemud_squared_0.5.153/etc/text/help/
circlemud_squared_0.5.153/src/util/
circlemud_squared_0.5.153/src/util/worldconv/
/**
 * @file act_item.c
 * @ingroup common
 *
 * Object handling routines -- get/drop and container handling
 *
 * @author Part of CircleMUD
 *
 * @par Copyright:
 *   Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br>
 *   CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
 *
 * @par
 *   All rights reserved.  See license.doc for complete information.
 *
 * @package cs
 * @version 1.0
 */

#include "structs.h"
#include "log.h"
#include "character.h"

#include "utils.h"
#include "comm.h"
#include "command.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "constants.h"
#include "base.h"
#include "room.h"
#include "item.h"

/* local functions */
int can_take_obj(charData_t *ch, itemData_t *obj);
void get_check_money(charData_t *ch, itemData_t *obj);
int perform_get_from_room(charData_t *ch, itemData_t *obj);
void get_from_room(charData_t *ch, char *arg, int amount);
void perform_give_gold(charData_t *ch, charData_t *vict, int amount);
void perform_give(charData_t *ch, charData_t *vict, itemData_t *obj);
int perform_drop(charData_t *ch, itemData_t *obj, const commandData_t *cmd, const char *sname, roomData_t *RDR);
void perform_drop_gold(charData_t *ch, int amount, const commandData_t *cmd, roomData_t *RDR);
charData_t *give_find_vict(charData_t *ch, char *arg);
void weight_change_object(itemData_t *obj, int weight);
void perform_put(charData_t *ch, itemData_t *obj, itemData_t *cont);
void name_from_drinkcon(itemData_t *obj);
void get_from_container(charData_t *ch, itemData_t *cont, char *arg, int mode, int amount);
void name_to_drinkcon(itemData_t *obj, int type);
void wear_message(charData_t *ch, itemData_t *obj, int where);
void perform_wear(charData_t *ch, itemData_t *obj, int where);
int find_eq_pos(charData_t *ch, itemData_t *obj, char *arg);
void perform_get_from_container(charData_t *ch, itemData_t *obj, itemData_t *cont, int mode);
void perform_remove(charData_t *ch, int pos);

/**
 * Put an object into another object
 *
 * @param ch Character moving the object
 * @param obj Object to be moved
 * @param cont Object to move obj into
 * @returns none
 * @sa do_put
 */
void perform_put(charData_t *ch, itemData_t *obj, itemData_t *cont)
{

  if (GET_ITEM_WEIGHT(cont) + GET_ITEM_WEIGHT(obj) > GET_ITEM_VAL(cont, 0))
    act("$p won't fit in $P.", FALSE, ch, obj, cont, TO_CHAR);
  else if (ITEM_FLAGGED(obj, ITEM_NODROP) && IN_ROOM(cont) != NULL)
    act("You can't get $p out of your hand.", FALSE, ch, obj, NULL, TO_CHAR);
  else {
    itemData_fromChar(obj);
    itemData_toItem(obj, cont);

    act("$n puts $p in $P.", TRUE, ch, obj, cont, TO_ROOM);

    /* Yes, I realize this is strange until we have auto-equip on rent. -gg */
    if (ITEM_FLAGGED(obj, ITEM_NODROP) && !ITEM_FLAGGED(cont, ITEM_NODROP)) {
      SET_BIT(GET_ITEM_EXTRA(cont), ITEM_NODROP);
      act("You get a strange feeling as you put $p in $P.", FALSE,
                ch, obj, cont, TO_CHAR);
    } else
      act("You put $p in $P.", FALSE, ch, obj, cont, TO_CHAR);
  }
}

/**
 * PUT command
 * 
 * This command lets a user put one or more objects into another object
 *
 * 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.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 * @sa perform_put
 */
ACMD(do_put)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char arg3[MAX_INPUT_LENGTH];
  itemData_t *obj, *next_obj, *cont;
  charData_t *tmp_char;
  int obj_dotmode, cont_dotmode, found = 0, howmany = 1;
  char *theobj, *thecont;

  one_argument(two_arguments(argument, arg1, arg2), arg3);	/* three_arguments */

  if (*arg3 && isInteger(arg1, FALSE)) {
    howmany = atoi(arg1);
    theobj = arg2;
    thecont = arg3;
  } else {
    theobj = arg1;
    thecont = arg2;
  }
  obj_dotmode = find_all_dots(theobj);
  cont_dotmode = find_all_dots(thecont);

  if (!*theobj)
    send_to_char(ch, "Put what in what?\r\n");
  else if (cont_dotmode != FIND_INDIV)
    send_to_char(ch, "You can only put things into one container at a time.\r\n");
  else if (!*thecont) {
    send_to_char(ch, "What do you want to put %s in?\r\n", obj_dotmode == FIND_INDIV ? "it" : "them");
  } else {
    generic_find(thecont, FIND_ITEM_INV | FIND_ITEM_ROOM, ch, &tmp_char, &cont);
    if (!cont)
      send_to_char(ch, "You don't see %s %s here.\r\n", AN(thecont), thecont);
    else if (GET_ITEM_TYPE(cont) != ITEM_CONTAINER)
      act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
    else if (ITEMVAL_FLAGGED(cont, CONT_CLOSED))
      send_to_char(ch, "You'd better open it first!\r\n");
    else {
      if (obj_dotmode == FIND_INDIV) {	/* put <obj> <container> */
	if (!(obj = get_item_in_list_vis(ch, theobj, NULL, ch->carrying)))
	  send_to_char(ch, "You aren't carrying %s %s.\r\n", AN(theobj), theobj);
	else if (obj == cont && howmany == 1)
	  send_to_char(ch, "You attempt to fold it into itself, but fail.\r\n");
	else {
	  while (obj && howmany) {
	    next_obj = obj->nextContent;
            if (obj != cont) {
              howmany--;
	      perform_put(ch, obj, cont);
            }
	    obj = get_item_in_list_vis(ch, theobj, NULL, next_obj);
	  }
	}
      } else {
	for (obj = ch->carrying; obj; obj = next_obj) {
	  next_obj = obj->nextContent;
	  if (obj != cont && CAN_SEE_ITEM(ch, obj) &&
	      (obj_dotmode == FIND_ALL || isname(theobj, obj->name))) {
	    found = 1;
	    perform_put(ch, obj, cont);
	  }
	}
	if (!found) {
	  if (obj_dotmode == FIND_ALL)
	    send_to_char(ch, "You don't seem to have anything to put in it.\r\n");
	  else
	    send_to_char(ch, "You don't seem to have any %ss.\r\n", theobj);
	}
      }
    }
  }
}

/**
 * Check if a character can take an object
 *
 * This function checks to see if a character can take an object.
 *
 * This returns FALSE is the character has the max number of objects, max weight,
 * or obj does not have the TAKE flag set.
 *
 * @param ch Character to check
 * @param obj Object to check
 * @returns TRUE or FALSE
 */
int can_take_obj(charData_t *ch, itemData_t *obj)
{
  if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) {
    act("$p: you can't carry that many items.", FALSE, ch, obj, 0, TO_CHAR);
    return (0);
  } else if ((IS_CARRYING_W(ch) + GET_ITEM_WEIGHT(obj)) > CAN_CARRY_W(ch)) {
    act("$p: you can't carry that much weight.", FALSE, ch, obj, 0, TO_CHAR);
    return (0);
  } else if (!(CAN_WEAR(obj, ITEM_WEAR_TAKE))) {
    act("$p: you can't take that!", FALSE, ch, obj, 0, TO_CHAR);
    return (0);
  }
  return (1);
}

/**
 * Check if an object was MONEY and if so, display it's value
 *
 * @param ch Character who got the object
 * @param obj Object to check
 * @returns none
 */
void get_check_money(charData_t *ch, itemData_t *obj)
{
  int value = GET_ITEM_VAL(obj, 0);

  if (GET_ITEM_TYPE(obj) != ITEM_MONEY || value <= 0)
    return;

  itemData_extract(obj);

  GET_GOLD(ch) += value;

  if (value == 1)
    send_to_char(ch, "There was 1 coin.\r\n");
  else
    send_to_char(ch, "There were %d coins.\r\n", value);
}

/**
 * Gets an object from another object
 *
 * @param ch Character trying to get the object
 * @param obj Object the character is trying to get
 * @param cont Container the object is in
 * @param mode Type of GET to perform
 * @returns none
 * @sa get_from_container
 */
void perform_get_from_container(charData_t *ch, itemData_t *obj, itemData_t *cont, int mode)
{

  if (mode == FIND_ITEM_INV || can_take_obj(ch, obj)) {
    if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch))
      act("$p: you can't hold any more items.", FALSE, ch, obj, 0, TO_CHAR);
    else {
      itemData_fromItem(obj);
      itemData_toChar(obj, ch);
      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);
    }
  }
}

/**
 * Gets an object from another object specified by name
 *
 * @param ch Character trying to get the object
 * @param cont Container the object is in
 * @param arg String containing the keywords for the object to get
 * @param mode Type of GET to perform
 * @param howmany How many matching objects to get
 * @returns none
 * @sa perform_get_from_container
 */
void get_from_container(charData_t *ch, itemData_t *cont, char *arg, int mode, int howmany)
{
  itemData_t *obj, *next_obj;
  int obj_dotmode, found = 0;

  obj_dotmode = find_all_dots(arg);

  if (ITEMVAL_FLAGGED(cont, CONT_CLOSED))
    act("$p is closed.", FALSE, ch, cont, 0, TO_CHAR);
  else if (obj_dotmode == FIND_INDIV) {
    if (!(obj = get_item_in_list_vis(ch, arg, NULL, cont->contains))) {
      char buf[MAX_STRING_LENGTH];

      snprintf(buf, sizeof(buf), "There doesn't seem to be %s %s in $p.", AN(arg), arg);
      act(buf, FALSE, ch, cont, 0, TO_CHAR);
    } else {
      itemData_t *obj_next;
      while (obj && howmany--) {
        obj_next = obj->nextContent;
        perform_get_from_container(ch, obj, cont, mode);
        obj = get_item_in_list_vis(ch, arg, NULL, obj_next);
      }
    }
  } else {
    if (obj_dotmode == FIND_ALLDOT && !*arg) {
      send_to_char(ch, "Get all of what?\r\n");
      return;
    }
    for (obj = cont->contains; obj; obj = next_obj) {
      next_obj = obj->nextContent;
      if (CAN_SEE_ITEM(ch, obj) &&
	  (obj_dotmode == FIND_ALL || isname(arg, obj->name))) {
	found = 1;
	perform_get_from_container(ch, obj, cont, mode);
      }
    }
    if (!found) {
      if (obj_dotmode == FIND_ALL)
	act("$p seems to be empty.", FALSE, ch, cont, 0, TO_CHAR);
      else {
        char buf[MAX_STRING_LENGTH];

	snprintf(buf, sizeof(buf), "You can't seem to find any %ss in $p.", arg);
	act(buf, FALSE, ch, cont, 0, TO_CHAR);
      }
    }
  }
}

/**
 * Gets an object from room character is in
 *
 * @param ch Character trying to get the object
 * @param obj Object the character is trying to get
 * @returns TRUE or FALSE if Character took the object
 * @sa get_from_room
 */
int perform_get_from_room(charData_t *ch, itemData_t *obj)
{
  if (can_take_obj(ch, obj)) {
    itemData_fromRoom(obj);
    itemData_toChar(obj, ch);
    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);
}

/**
 * Get an object from room character is in by name
 *
 * @param ch Character trying to get the object
 * @param arg String containing the keywords for the object to get
 * @param howmany How many matching objects to get
 * @returns none
 * @sa perform_get_from_room
 */
void get_from_room(charData_t *ch, char *arg, int howmany)
{
  itemData_t *obj, *next_obj;
  int dotmode, found = 0;

  dotmode = find_all_dots(arg);

  if (dotmode == FIND_INDIV) {
    if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->room->contents)))
      send_to_char(ch, "You don't see %s %s here.\r\n", AN(arg), arg);
    else {
      itemData_t *obj_next;
      while(obj && howmany--) {
	obj_next = obj->nextContent;
        perform_get_from_room(ch, obj);
        obj = get_item_in_list_vis(ch, arg, NULL, obj_next);
      }
    }
  } else {
    if (dotmode == FIND_ALLDOT && !*arg) {
      send_to_char(ch, "Get all of what?\r\n");
      return;
    }
    for (obj = ch->room->contents; obj; obj = next_obj) {
      next_obj = obj->nextContent;
      if (CAN_SEE_ITEM(ch, obj) &&
	  (dotmode == FIND_ALL || isname(arg, obj->name))) {
	found = 1;
	perform_get_from_room(ch, obj);
      }
    }
    if (!found) {
      if (dotmode == FIND_ALL)
	send_to_char(ch, "There doesn't seem to be anything here.\r\n");
      else
	send_to_char(ch, "You don't see any %ss here.\r\n", arg);
    }
  }
}

/**
 * GET command
 *
 * This command lets the character get an object
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_get)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char arg3[MAX_INPUT_LENGTH];

  int cont_dotmode, found = 0, mode;
  itemData_t *cont;
  charData_t *tmp_char;

  one_argument(two_arguments(argument, arg1, arg2), arg3);	/* three_arguments */

  if (!*arg1)
    send_to_char(ch, "Get what?\r\n");
  else if (!*arg2)
    get_from_room(ch, arg1, 1);
  else if (isInteger(arg1, FALSE) && !*arg3)
    get_from_room(ch, arg2, atoi(arg1));
  else {
    int amount = 1;
    if (isInteger(arg1, FALSE)) {
      amount = atoi(arg1);
      strcpy(arg1, arg2);	/* strcpy: OK (sizeof: arg1 == arg2) */
      strcpy(arg2, arg3);	/* strcpy: OK (sizeof: arg2 == arg3) */
    }
    cont_dotmode = find_all_dots(arg2);
    if (cont_dotmode == FIND_INDIV) {
      mode = generic_find(arg2, FIND_ITEM_INV | FIND_ITEM_ROOM, ch, &tmp_char, &cont);
      if (!cont)
	send_to_char(ch, "You don't have %s %s.\r\n", AN(arg2), arg2);
      else if (GET_ITEM_TYPE(cont) != ITEM_CONTAINER)
	act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
      else
	get_from_container(ch, cont, arg1, mode, amount);
    } else {
      if (cont_dotmode == FIND_ALLDOT && !*arg2) {
	send_to_char(ch, "Get from all of what?\r\n");
	return;
      }
      for (cont = ch->carrying; cont; cont = cont->nextContent)
	if (CAN_SEE_ITEM(ch, cont) &&
	    (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) {
	  if (GET_ITEM_TYPE(cont) == ITEM_CONTAINER) {
	    found = 1;
	    get_from_container(ch, cont, arg1, FIND_ITEM_INV, amount);
	  } else if (cont_dotmode == FIND_ALLDOT) {
	    found = 1;
	    act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
	  }
	}
      for (cont = ch->room->contents; cont; cont = cont->nextContent)
	if (CAN_SEE_ITEM(ch, cont) &&
	    (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) {
	  if (GET_ITEM_TYPE(cont) == ITEM_CONTAINER) {
	    get_from_container(ch, cont, arg1, FIND_ITEM_ROOM, amount);
	    found = 1;
	  } else if (cont_dotmode == FIND_ALLDOT) {
	    act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR);
	    found = 1;
	  }
	}
      if (!found) {
	if (cont_dotmode == FIND_ALL)
	  send_to_char(ch, "You can't seem to find any containers.\r\n");
	else
	  send_to_char(ch, "You can't seem to find any %ss here.\r\n", arg2);
      }
    }
  }
}

/**
 * Create and DROP a MONEY item
 *
 * @param ch Character dropping the MONEY item
 * @param amount Amount of gold to set the value to
 * @param cmd Command which has called perform_drop_gold
 * @param RDR Donation room
 * @returns none
 * @sa do_drop
 */
void perform_drop_gold(charData_t *ch, int amount, const commandData_t *cmd, roomData_t *RDR)
{
  itemData_t *obj;

  if (amount <= 0)
    send_to_char(ch, "Heh heh heh.. we are jolly funny today, eh?\r\n");
  else if (GET_GOLD(ch) < amount)
    send_to_char(ch, "You don't have that many coins!\r\n");
  else {
    if (CMD_IS("junk")) {
      WAIT_STATE(ch, PULSE_VIOLENCE);	/* to prevent coin-bombing */
      obj = create_money(amount);
      if (CMD_IS("donate")) {
	send_to_char(ch, "You throw some gold into the air where it disappears in a puff of smoke!\r\n");
	act("$n throws some gold into the air where it disappears in a puff of smoke!",
	    FALSE, ch, 0, 0, TO_ROOM);
	itemData_toRoom(obj, RDR);
	act("$p suddenly appears in a puff of orange smoke!", 0, 0, obj, 0, TO_ROOM);
      } else {
        char buf[MAX_STRING_LENGTH];

	snprintf(buf, sizeof(buf), "$n drops %s.", money_desc(amount));
	act(buf, TRUE, ch, 0, 0, TO_ROOM);

	send_to_char(ch, "You drop some gold.\r\n");
	itemData_toRoom(obj, IN_ROOM(ch));
      }
    } else {
      char buf[MAX_STRING_LENGTH];

      snprintf(buf, sizeof(buf), "$n drops %s which disappears in a puff of smoke!", money_desc(amount));
      act(buf, FALSE, ch, 0, 0, TO_ROOM);

      send_to_char(ch, "You drop some gold which disappears in a puff of smoke!\r\n");
    }
    GET_GOLD(ch) -= amount;
  }
}


#define VANISH(cmd) ( ( CMD_IS("donate") || CMD_IS("junk") ) ? "  It vanishes in a puff of smoke!" : "")

/**
 * Remove an object from a character's inventory
 *
 * @param ch Character performing the drop
 * @param obj Object being dropped
 * @param cmd Command which has called to perform_dorp
 * @param sname String value for type of drop (drop, donate, junk, etc)
 * @param RDR Donation room
 * @returns TRUE or FALSE if the object was dropped
 * @sa do_drop
 */
int perform_drop(charData_t *ch, itemData_t *obj, const commandData_t *cmd, const char *sname, roomData_t *RDR)
{
  char buf[MAX_STRING_LENGTH];
  int value;

  if (ITEM_FLAGGED(obj, ITEM_NODROP)) {
    snprintf(buf, sizeof(buf), "You can't %s $p, it must be CURSED!", sname);
    act(buf, FALSE, ch, obj, 0, TO_CHAR);
    return (0);
  }

  snprintf(buf, sizeof(buf), "You %s $p.%s", sname, VANISH(mode));
  act(buf, FALSE, ch, obj, 0, TO_CHAR);

  snprintf(buf, sizeof(buf), "$n %ss $p.%s", sname, VANISH(mode));
  act(buf, TRUE, ch, obj, 0, TO_ROOM);

  itemData_fromChar(obj);

  if ((CMD_IS("donate")) && ITEM_FLAGGED(obj, ITEM_NODONATE))
    cmd = find_command("junk");

  if (CMD_IS("drop")) {
    itemData_toRoom(obj, IN_ROOM(ch));
    return (0);
  } else if (CMD_IS("donate")) {
    itemData_toRoom(obj, RDR);
    act("$p suddenly appears in a puff a smoke!", FALSE, 0, obj, 0, TO_ROOM);
    return (0);
  } else if (CMD_IS("junk")) {
    value = MAX(1, MIN(200, GET_ITEM_COST(obj) / 16));
    itemData_extract(obj);
    return (value);
  } else 
    log("SYSERR: Incorrect command '%s' passed to perform_drop.", CMD_NAME);

  return (0);
}

/**
 * DROP, DONATE, JUNK commands
 *
 * These commands let the user get rid of one or more objects from their inventory
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_drop)
{
  char arg[MAX_INPUT_LENGTH];
  itemData_t *obj, *next_obj;
  roomData_t *RDR = NULL;
  const commandData_t *doCmd = find_command("drop");
  int dotmode, amount = 0, multi;
  const char *sname;

  if (CMD_IS("junk")) {
    sname = "junk";
    doCmd = find_command("junk");
  } else if (CMD_IS("donate")) {
    sname = "donate";
    doCmd = find_command("donate");
    switch (rand_number(0, 2)) {
    case 0:
      doCmd = find_command("junk");
      break;
    case 1:
    case 2:
      RDR = roomData_find("midgaardNorth:3001");
      break;
    }
    if (RDR == NULL) {
      send_to_char(ch, "Sorry, you can't donate anything right now.\r\n");
      return;
    }
  } else {
    sname = "drop";
  }

  argument = one_argument(argument, arg);

  if (!*arg) {
    send_to_char(ch, "What do you want to %s?\r\n", sname);
    return;
  } else if (isInteger(arg, FALSE)) {
    multi = atoi(arg);
    one_argument(argument, arg);
    if (!str_cmp("coins", arg) || !str_cmp("coin", arg))
      perform_drop_gold(ch, multi, doCmd, RDR);
    else if (multi <= 0)
      send_to_char(ch, "Yeah, that makes sense.\r\n");
    else if (!*arg)
      send_to_char(ch, "What do you want to %s %d of?\r\n", sname, multi);
    else if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
      send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);
    else {
      do {
        next_obj = get_item_in_list_vis(ch, arg, NULL, obj->nextContent);
        amount += perform_drop(ch, obj, doCmd, sname, RDR);
        obj = next_obj;
      } while (obj && --multi);
    }
  } else {
    dotmode = find_all_dots(arg);

    /* Can't junk or donate all */
    if ((dotmode == FIND_ALL) && (CMD_IS("junk") || CMD_IS("donate"))) {
      if (CMD_IS("junk"))
	send_to_char(ch, "Go to the dump if you want to junk EVERYTHING!\r\n");
      else
	send_to_char(ch, "Go do the donation room if you want to donate EVERYTHING!\r\n");
      return;
    }
    if (dotmode == FIND_ALL) {
      if (!ch->carrying)
	send_to_char(ch, "You don't seem to be carrying anything.\r\n");
      else
	for (obj = ch->carrying; obj; obj = next_obj) {
	  next_obj = obj->nextContent;
	  amount += perform_drop(ch, obj, doCmd, sname, RDR);
	}
    } else if (dotmode == FIND_ALLDOT) {
      if (!*arg) {
	send_to_char(ch, "What do you want to %s all of?\r\n", sname);
	return;
      }
      if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
	send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);

      while (obj) {
	next_obj = get_item_in_list_vis(ch, arg, NULL, obj->nextContent);
	amount += perform_drop(ch, obj, doCmd, sname, RDR);
	obj = next_obj;
      }
    } else {
      if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
	send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
      else
	amount += perform_drop(ch, obj, doCmd, sname, RDR);
    }
  }

  if (amount && (CMD_IS("junk"))) {
    send_to_char(ch, "You have been rewarded by the gods!\r\n");
    act("$n has been rewarded by the gods!", TRUE, ch, 0, 0, TO_ROOM);
    GET_GOLD(ch) += amount;
  }
}

/**
 * Move an object from one character to another
 *
 * @param ch Character with the object
 * @param vict Character to receive the object
 * @param obj Object to be given
 * @returns none
 */
void perform_give(charData_t *ch, charData_t *vict, itemData_t *obj)
{
  if (ITEM_FLAGGED(obj, 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_ITEM_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;
  }
  itemData_fromChar(obj);
  itemData_toChar(obj, vict);
  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);
}

/**
 * Find a character by name in the room
 *
 * @param ch Character trying to find the victim by name
 * @param arg String contianing the name of the character to find
 * @returns Pointer to the Charater or NULL
 */
charData_t *give_find_vict(charData_t *ch, char *arg)
{
  charData_t *vict;

  skip_spaces(&arg);
  if (!*arg)
    send_to_char(ch, "To who?\r\n");
  else if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM)))
    send_to_char(ch, "%s", NOPERSON);
  else if (vict == ch)
    send_to_char(ch, "What's the point of that?\r\n");
  else
    return (vict);

  return (NULL);
}

/**
 * Transfer an amount of gold from one character to another
 *
 * @param ch Character giving the Gold
 * @param vict Character receiving the Gold
 * @param amount Amount of gold to give
 * @returns none
 */
void perform_give_gold(charData_t *ch, charData_t *vict, int amount)
{
  char buf[MAX_STRING_LENGTH];

  if (amount <= 0) {
    send_to_char(ch, "Heh heh heh ... we are jolly funny today, eh?\r\n");
    return;
  }
  if (GET_GOLD(ch) < amount && (IS_NPC(ch) || GET_AUTH(ch) < AUTH_OWNER)) {
    send_to_char(ch, "You don't have that many coins!\r\n");
    return;
  }
  send_to_char(ch, "%s", OK);

  snprintf(buf, sizeof(buf), "$n gives you %d gold coin%s.", amount, amount == 1 ? "" : "s");
  act(buf, FALSE, ch, 0, vict, TO_VICT);

  snprintf(buf, sizeof(buf), "$n gives %s to $N.", money_desc(amount));
  act(buf, TRUE, ch, 0, vict, TO_NOTVICT);

  if (IS_NPC(ch) || GET_AUTH(ch) < AUTH_OWNER)
    GET_GOLD(ch) -= amount;
  GET_GOLD(vict) += amount;
}

/**
 * GIVE command
 *
 * This command lets a character give an object to another character
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none 
 */
ACMD(do_give)
{
  char arg[MAX_STRING_LENGTH];
  int amount, dotmode;
  charData_t *vict;
  itemData_t *obj, *next_obj;

  argument = one_argument(argument, arg);

  if (!*arg)
    send_to_char(ch, "Give what to who?\r\n");
  else if (isInteger(arg, FALSE)) {
    amount = atoi(arg);
    argument = one_argument(argument, arg);
    if (!str_cmp("coins", arg) || !str_cmp("coin", arg)) {
      one_argument(argument, arg);
      if ((vict = give_find_vict(ch, arg)) != NULL)
	perform_give_gold(ch, vict, amount);
      return;
    } else if (!*arg)	/* Give multiple code. */
      send_to_char(ch, "What do you want to give %d of?\r\n", amount);
    else if (!(vict = give_find_vict(ch, argument)))
      return;
    else if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying))) 
      send_to_char(ch, "You don't seem to have any %ss.\r\n", arg);
    else {
      while (obj && amount--) {
	next_obj = get_item_in_list_vis(ch, arg, NULL, obj->nextContent);
	perform_give(ch, vict, obj);
	obj = next_obj;
      }
    }
  } else {
    char buf1[MAX_INPUT_LENGTH];

    one_argument(argument, buf1);
    if (!(vict = give_find_vict(ch, buf1)))
      return;
    dotmode = find_all_dots(arg);
    if (dotmode == FIND_INDIV) {
      if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
	send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
      else
	perform_give(ch, vict, obj);
    } else {
      if (dotmode == FIND_ALLDOT && !*arg) {
	send_to_char(ch, "All of what?\r\n");
	return;
      }
      if (!ch->carrying)
	send_to_char(ch, "You don't seem to be holding anything.\r\n");
      else
	for (obj = ch->carrying; obj; obj = next_obj) {
	  next_obj = obj->nextContent;
	  if (CAN_SEE_ITEM(ch, obj) &&
	      ((dotmode == FIND_ALL || isname(arg, obj->name))))
	    perform_give(ch, vict, obj);
	}
    }
  }
}

/**
 * Alter an object's weight
 *
 * @param obj Object to alter
 * @param weight Amount to alter the object's weight by
 * @returns none
 */
void weight_change_object(itemData_t *obj, int weight)
{
  itemData_t *tmp_obj;
  charData_t *tmp_ch;

  if (IN_ROOM(obj) != NULL) {
    GET_ITEM_WEIGHT(obj) += weight;
  } else if ((tmp_ch = obj->carriedBy)) {
    itemData_fromChar(obj);
    GET_ITEM_WEIGHT(obj) += weight;
    itemData_toChar(obj, tmp_ch);
  } else if ((tmp_obj = obj->inObj)) {
    itemData_fromItem(obj);
    GET_ITEM_WEIGHT(obj) += weight;
    itemData_toItem(obj, tmp_obj);
  } else {
    log("SYSERR: Unknown attempt to subtract weight from an object.");
  }
}

/**
 * Update the name of a drink container to remove the drink type
 *
 * @param obj Drink container object to update
 * @returns none
 */
void name_from_drinkcon(itemData_t *obj)
{
  char *new_name, *cur_name, *next;
  const char *liqname;
  int liqlen, cpylen;

  if (!obj || (GET_ITEM_TYPE(obj) != ITEM_DRINKCON && GET_ITEM_TYPE(obj) != ITEM_FOUNTAIN))
    return;

  liqname = drinknames[GET_ITEM_VAL(obj, 2)];
  if (!isname(liqname, obj->name)) {
    log("SYSERR: Can't remove liquid '%s' from '%s' (%s:%d) item.", liqname, obj->name, obj->zone->keyword, obj->vnum);
    return;
  }

  liqlen = strlen(liqname);
  CREATE(new_name, char, strlen(obj->name) - strlen(liqname)); /* +1 for NUL, -1 for space */

  for (cur_name = obj->name; cur_name; cur_name = next) {
    if (*cur_name == ' ')
      cur_name++;

    if ((next = strchr(cur_name, ' ')))
      cpylen = next - cur_name;
    else
      cpylen = strlen(cur_name);

    if (!strn_cmp(cur_name, liqname, liqlen))
      continue;

    if (*new_name)
      strcat(new_name, " ");	/* strcat: OK (size precalculated) */
    strncat(new_name, cur_name, cpylen);	/* strncat: OK (size precalculated) */
  }

  if (DIFFERS(obj, name))
    free(obj->name);
  obj->name = new_name;
}

/**
 * Update the name of a drink container to include the drink type
 *
 * @param obj Drink container object to update
 * @param type Drink type
 * @returns none
 */
void name_to_drinkcon(itemData_t *obj, int type)
{
  char *new_name;

  if (!obj || (GET_ITEM_TYPE(obj) != ITEM_DRINKCON && GET_ITEM_TYPE(obj) != ITEM_FOUNTAIN))
    return;

  CREATE(new_name, char, strlen(obj->name) + strlen(drinknames[type]) + 2);
  sprintf(new_name, "%s %s", obj->name, drinknames[type]);	/* sprintf: OK */

  if (DIFFERS(obj, name))
    free(obj->name);

  obj->name = new_name;
}

/**
 * DRINK command
 *
 * This command lets the user drink from a drink container or fountain
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_drink)
{
  char arg[MAX_INPUT_LENGTH];
  itemData_t *temp;
  effectData_t af;
  int amount, weight;
  int on_ground = 0;

  one_argument(argument, arg);

  if (IS_NPC(ch))	/* Cannot use GET_COND() on mobs. */
    return;

  if (!*arg) {
    send_to_char(ch, "Drink from what?\r\n");
    return;
  }
  if (!(temp = get_item_in_list_vis(ch, arg, NULL, ch->carrying))) {
    if (!(temp = get_item_in_list_vis(ch, arg, NULL, ch->room->contents))) {
      send_to_char(ch, "You can't find it!\r\n");
      return;
    } else
      on_ground = 1;
  }
  if ((GET_ITEM_TYPE(temp) != ITEM_DRINKCON) &&
      (GET_ITEM_TYPE(temp) != ITEM_FOUNTAIN)) {
    send_to_char(ch, "You can't drink from that!\r\n");
    return;
  }
  if (on_ground && (GET_ITEM_TYPE(temp) == ITEM_DRINKCON)) {
    send_to_char(ch, "You have to be holding that to drink from it.\r\n");
    return;
  }
  if ((GET_COND(ch, DRUNK) > 10) && (GET_COND(ch, THIRST) > 0)) {
    /* The pig is drunk */
    send_to_char(ch, "You can't seem to get close enough to your mouth.\r\n");
    act("$n tries to drink but misses $s mouth!", TRUE, ch, 0, 0, TO_ROOM);
    return;
  }
  if ((GET_COND(ch, FULL) > 20) && (GET_COND(ch, THIRST) > 0)) {
    send_to_char(ch, "Your stomach can't contain anymore!\r\n");
    return;
  }
  if (!GET_ITEM_VAL(temp, 1)) {
    send_to_char(ch, "It's empty.\r\n");
    return;
  }
  if (CMD_IS("drink")) {
    char buf[MAX_STRING_LENGTH];

    snprintf(buf, sizeof(buf), "$n drinks %s from $p.", drinks[GET_ITEM_VAL(temp, 2)]);
    act(buf, TRUE, ch, temp, 0, TO_ROOM);

    send_to_char(ch, "You drink the %s.\r\n", drinks[GET_ITEM_VAL(temp, 2)]);

    if (drink_aff[GET_ITEM_VAL(temp, 2)][DRUNK] > 0)
      amount = (25 - GET_COND(ch, THIRST)) / drink_aff[GET_ITEM_VAL(temp, 2)][DRUNK];
    else
      amount = rand_number(3, 10);

  } else {
    act("$n sips from $p.", TRUE, ch, temp, 0, TO_ROOM);
    send_to_char(ch, "It tastes like %s.\r\n", drinks[GET_ITEM_VAL(temp, 2)]);
    amount = 1;
  }

  amount = MIN(amount, GET_ITEM_VAL(temp, 1));

  /* You can't subtract more than the object weighs */
  weight = MIN(amount, GET_ITEM_WEIGHT(temp));

  weight_change_object(temp, -weight);	/* Subtract amount */

  gain_condition(ch, DRUNK,  drink_aff[GET_ITEM_VAL(temp, 2)][DRUNK]  * amount / 4);
  gain_condition(ch, FULL,   drink_aff[GET_ITEM_VAL(temp, 2)][FULL]   * amount / 4);
  gain_condition(ch, THIRST, drink_aff[GET_ITEM_VAL(temp, 2)][THIRST] * amount / 4);

  if (GET_COND(ch, DRUNK) > 10)
    send_to_char(ch, "You feel drunk.\r\n");

  if (GET_COND(ch, THIRST) > 20)
    send_to_char(ch, "You don't feel thirsty any more.\r\n");

  if (GET_COND(ch, FULL) > 20)
    send_to_char(ch, "You are full.\r\n");

  if (GET_ITEM_VAL(temp, 3)) {	/* The crap was poisoned ! */
    send_to_char(ch, "Oops, it tasted rather strange!\r\n");
    act("$n chokes and utters some strange sounds.", TRUE, ch, 0, 0, TO_ROOM);

    af.type = SPELL_POISON;
    af.duration = amount * 3;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = AFF_POISON;
    effectData_join(ch, &af, FALSE, FALSE, FALSE, FALSE);
  }
  /* empty the container, and no longer poison. */
  GET_ITEM_VAL(temp, 1) -= amount;
  if (!GET_ITEM_VAL(temp, 1)) {	/* The last bit */
    name_from_drinkcon(temp);
    GET_ITEM_VAL(temp, 2) = 0;
    GET_ITEM_VAL(temp, 3) = 0;
  }
  return;
}

/**
 * EAT command
 *
 * This command lets the user eat FOOD objects
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_eat)
{
  char arg[MAX_INPUT_LENGTH];
  itemData_t *food;
  effectData_t af;
  int amount;

  one_argument(argument, arg);

  if (IS_NPC(ch))	/* Cannot use GET_COND() on mobs. */
    return;

  if (!*arg) {
    send_to_char(ch, "Eat what?\r\n");
    return;
  }
  if (!(food = get_item_in_list_vis(ch, arg, NULL, ch->carrying))) {
    send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
    return;
  }
  if (CMD_IS("taste") && ((GET_ITEM_TYPE(food) == ITEM_DRINKCON) ||
			       (GET_ITEM_TYPE(food) == ITEM_FOUNTAIN))) {
    do_drink(ch, argument, 0);
    return;
  }
  if (GET_ITEM_TYPE(food) != ITEM_FOOD && GET_AUTH(ch) < AUTH_OWNER) {
    send_to_char(ch, "You can't eat THAT!\r\n");
    return;
  }
  if (GET_COND(ch, FULL) > 20) {/* Stomach full */
    send_to_char(ch, "You are too full to eat more!\r\n");
    return;
  }
  if (CMD_IS("eat")) {
    act("You eat $p.", FALSE, ch, food, 0, TO_CHAR);
    act("$n eats $p.", TRUE, ch, food, 0, TO_ROOM);
  } else {
    act("You nibble a little bit of $p.", FALSE, ch, food, 0, TO_CHAR);
    act("$n tastes a little bit of $p.", TRUE, ch, food, 0, TO_ROOM);
  }

  amount = (CMD_IS("eat") ? GET_ITEM_VAL(food, 0) : 1);

  gain_condition(ch, FULL, amount);

  if (GET_COND(ch, FULL) > 20)
    send_to_char(ch, "You are full.\r\n");

  if (GET_ITEM_VAL(food, 3) && GET_AUTH(ch) < AUTH_WIZARD) {
    /* The crap was poisoned ! */
    send_to_char(ch, "Oops, that tasted rather strange!\r\n");
    act("$n coughs and utters some strange sounds.", FALSE, ch, 0, 0, TO_ROOM);

    af.type = SPELL_POISON;
    af.duration = amount * 2;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = AFF_POISON;
    effectData_join(ch, &af, FALSE, FALSE, FALSE, FALSE);
  }
  if (CMD_IS("eat"))
    itemData_extract(food);
  else {
    if (!(--GET_ITEM_VAL(food, 0))) {
      send_to_char(ch, "There's nothing left now.\r\n");
      itemData_extract(food);
    }
  }
}

/**
 * POUR and FILL commands
 *
 * This command lets the user fill or empty a drink container
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_pour)
{
  char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
  itemData_t *from_obj = NULL, *to_obj = NULL;
  int amount;

  two_arguments(argument, arg1, arg2);

  if (CMD_IS("pour")) {
    if (!*arg1) {		/* No arguments */
      send_to_char(ch, "From what do you want to pour?\r\n");
      return;
    }
    if (!(from_obj = get_item_in_list_vis(ch, arg1, NULL, ch->carrying))) {
      send_to_char(ch, "You can't find it!\r\n");
      return;
    }
    if (GET_ITEM_TYPE(from_obj) != ITEM_DRINKCON) {
      send_to_char(ch, "You can't pour from that!\r\n");
      return;
    }
  }
  if (CMD_IS("fill")) {
    if (!*arg1) {		/* no arguments */
      send_to_char(ch, "What do you want to fill?  And what are you filling it from?\r\n");
      return;
    }
    if (!(to_obj = get_item_in_list_vis(ch, arg1, NULL, ch->carrying))) {
      send_to_char(ch, "You can't find it!\r\n");
      return;
    }
    if (GET_ITEM_TYPE(to_obj) != ITEM_DRINKCON) {
      act("You can't fill $p!", FALSE, ch, to_obj, 0, TO_CHAR);
      return;
    }
    if (!*arg2) {		/* no 2nd argument */
      act("What do you want to fill $p from?", FALSE, ch, to_obj, 0, TO_CHAR);
      return;
    }
    if (!(from_obj = get_item_in_list_vis(ch, arg2, NULL, ch->room->contents))) {
      send_to_char(ch, "There doesn't seem to be %s %s here.\r\n", AN(arg2), arg2);
      return;
    }
    if (GET_ITEM_TYPE(from_obj) != ITEM_FOUNTAIN) {
      act("You can't fill something from $p.", FALSE, ch, from_obj, 0, TO_CHAR);
      return;
    }
  }
  if (GET_ITEM_VAL(from_obj, 1) == 0) {
    act("The $p is empty.", FALSE, ch, from_obj, 0, TO_CHAR);
    return;
  }
  if (CMD_IS("pour")) {	/* pour */
    if (!*arg2) {
      send_to_char(ch, "Where do you want it?  Out or in what?\r\n");
      return;
    }
    if (!str_cmp(arg2, "out")) {
      act("$n empties $p.", TRUE, ch, from_obj, 0, TO_ROOM);
      act("You empty $p.", FALSE, ch, from_obj, 0, TO_CHAR);

      weight_change_object(from_obj, -GET_ITEM_VAL(from_obj, 1)); /* Empty */

      name_from_drinkcon(from_obj);
      GET_ITEM_VAL(from_obj, 1) = 0;
      GET_ITEM_VAL(from_obj, 2) = 0;
      GET_ITEM_VAL(from_obj, 3) = 0;

      return;
    }
    if (!(to_obj = get_item_in_list_vis(ch, arg2, NULL, ch->carrying))) {
      send_to_char(ch, "You can't find it!\r\n");
      return;
    }
    if ((GET_ITEM_TYPE(to_obj) != ITEM_DRINKCON) &&
	(GET_ITEM_TYPE(to_obj) != ITEM_FOUNTAIN)) {
      send_to_char(ch, "You can't pour anything into that.\r\n");
      return;
    }
  }
  if (to_obj == from_obj) {
    send_to_char(ch, "A most unproductive effort.\r\n");
    return;
  }
  if ((GET_ITEM_VAL(to_obj, 1) != 0) &&
      (GET_ITEM_VAL(to_obj, 2) != GET_ITEM_VAL(from_obj, 2))) {
    send_to_char(ch, "There is already another liquid in it!\r\n");
    return;
  }
  if (!(GET_ITEM_VAL(to_obj, 1) < GET_ITEM_VAL(to_obj, 0))) {
    send_to_char(ch, "There is no room for more.\r\n");
    return;
  }
  if (CMD_IS("pour"))
    send_to_char(ch, "You pour the %s into the %s.", drinks[GET_ITEM_VAL(from_obj, 2)], arg2);

  if (CMD_IS("fill")) {
    act("You gently fill $p from $P.", FALSE, ch, to_obj, from_obj, TO_CHAR);
    act("$n gently fills $p from $P.", TRUE, ch, to_obj, from_obj, TO_ROOM);
  }
  /* New alias */
  if (GET_ITEM_VAL(to_obj, 1) == 0)
    name_to_drinkcon(to_obj, GET_ITEM_VAL(from_obj, 2));

  /* First same type liq. */
  GET_ITEM_VAL(to_obj, 2) = GET_ITEM_VAL(from_obj, 2);

  /* Then how much to pour */
  GET_ITEM_VAL(from_obj, 1) -= (amount =
			 (GET_ITEM_VAL(to_obj, 0) - GET_ITEM_VAL(to_obj, 1)));

  GET_ITEM_VAL(to_obj, 1) = GET_ITEM_VAL(to_obj, 0);

  if (GET_ITEM_VAL(from_obj, 1) < 0) {	/* There was too little */
    GET_ITEM_VAL(to_obj, 1) += GET_ITEM_VAL(from_obj, 1);
    amount += GET_ITEM_VAL(from_obj, 1);
    name_from_drinkcon(from_obj);
    GET_ITEM_VAL(from_obj, 1) = 0;
    GET_ITEM_VAL(from_obj, 2) = 0;
    GET_ITEM_VAL(from_obj, 3) = 0;
  }
  /* Then the poison boogie */
  GET_ITEM_VAL(to_obj, 3) =
    (GET_ITEM_VAL(to_obj, 3) || GET_ITEM_VAL(from_obj, 3));

  /* And the weight boogie */
  weight_change_object(from_obj, -amount);
  weight_change_object(to_obj, amount);	/* Add weight */
}

/**
 * Display WEAR messages for an object
 *
 * @param ch Character wearing the object
 * @param obj Object being worn
 * @param where Location where the object is being worn
 * @returns none
 */
void wear_message(charData_t *ch, itemData_t *obj, int where)
{
  const char *wear_messages[][2] = {
    { "$n lights $p and holds it."			, "You light $p and hold it."			},
    { "$n slides $p on to $s right ring finger."	, "You slide $p on to your right ring finger."	},
    { "$n slides $p on to $s left ring finger."		, "You slide $p on to your left ring finger."	},
    { "$n wears $p around $s neck."			, "You wear $p around your neck."},
    { "$n wears $p around $s neck."			, "You wear $p around your neck."},
    { "$n wears $p on $s body."				, "You wear $p on your body."},
    { "$n wears $p on $s head."				, "You wear $p on your head."},
    { "$n puts $p on $s legs."				, "You put $p on your legs."},
    { "$n wears $p on $s feet."				, "You wear $p on your feet."},
    { "$n puts $p on $s hands."				, "You put $p on your hands."},
    { "$n wears $p on $s arms."				, "You wear $p on your arms."},
    { "$n straps $p around $s arm as a shield."		, "You start to use $p as a shield."},
    { "$n wears $p about $s body."			, "You wear $p around your body."},
    { "$n wears $p around $s waist."			, "You wear $p around your waist."},
    { "$n puts $p on around $s right wrist."		, "You put $p on around your right wrist."},
    { "$n puts $p on around $s left wrist."		, "You put $p on around your left wrist."},
    { "$n wields $p."					, "You wield $p."},
    { "$n grabs $p."					, "You grab $p."}
  };

  act(wear_messages[where][0], TRUE, ch, obj, 0, TO_ROOM);
  act(wear_messages[where][1], FALSE, ch, obj, 0, TO_CHAR);
}

/**
 * Put an object into a character's worn equipment
 *
 * @param ch Character wearing the object
 * @param obj Object to be worn
 * @param where Location where the object is to be worn
 * @returns none
 */
void perform_wear(charData_t *ch, itemData_t *obj, int where)
{
  /*
   * ITEM_WEAR_TAKE is used for objects that do not require special bits
   * to be put into that position (e.g. you can hold any object, not just
   * an object with a HOLD bit.)
   */

  int wear_bitvectors[] = {
    ITEM_WEAR_TAKE, ITEM_WEAR_FINGER, ITEM_WEAR_FINGER, ITEM_WEAR_NECK,
    ITEM_WEAR_NECK, ITEM_WEAR_BODY, ITEM_WEAR_HEAD, ITEM_WEAR_LEGS,
    ITEM_WEAR_FEET, ITEM_WEAR_HANDS, ITEM_WEAR_ARMS, ITEM_WEAR_SHIELD,
    ITEM_WEAR_ABOUT, ITEM_WEAR_WAIST, ITEM_WEAR_WRIST, ITEM_WEAR_WRIST,
    ITEM_WEAR_WIELD, ITEM_WEAR_TAKE
  };

  const char *already_wearing[] = {
    "You're already using a light.\r\n",
    "YOU SHOULD NEVER SEE THIS MESSAGE.  PLEASE REPORT.\r\n",
    "You're already wearing something on both of your ring fingers.\r\n",
    "YOU SHOULD NEVER SEE THIS MESSAGE.  PLEASE REPORT.\r\n",
    "You can't wear anything else around your neck.\r\n",
    "You're already wearing something on your body.\r\n",
    "You're already wearing something on your head.\r\n",
    "You're already wearing something on your legs.\r\n",
    "You're already wearing something on your feet.\r\n",
    "You're already wearing something on your hands.\r\n",
    "You're already wearing something on your arms.\r\n",
    "You're already using a shield.\r\n",
    "You're already wearing something about your body.\r\n",
    "You already have something around your waist.\r\n",
    "YOU SHOULD NEVER SEE THIS MESSAGE.  PLEASE REPORT.\r\n",
    "You're already wearing something around both of your wrists.\r\n",
    "You're already wielding a weapon.\r\n",
    "You're already holding something.\r\n"
  };

  /* first, make sure that the wear position is valid. */
  if (!CAN_WEAR(obj, wear_bitvectors[where])) {
    act("You can't wear $p there.", FALSE, ch, obj, 0, TO_CHAR);
    return;
  }
  /* for neck, finger, and wrist, try pos 2 if pos 1 is already full */
  if ((where == WEAR_FINGER_R) || (where == WEAR_NECK_1) || (where == WEAR_WRIST_R))
    if (GET_EQ(ch, where))
      where++;

  if (GET_EQ(ch, where)) {
    send_to_char(ch, "%s", already_wearing[where]);
    return;
  }
  wear_message(ch, obj, where);
  itemData_fromChar(obj);
  char_equipItem(ch, obj, where);
}

/**
 * Find an equipment position by name
 *
 * @param ch Character to find the equipment position on
 * @param obj Object to be placed in that position
 * @param arg String containing the wear location by name
 * @returns Numeric equipment position or -1
 */
int find_eq_pos(charData_t *ch, itemData_t *obj, char *arg)
{
  int where = -1;

  const char *keywords[] = {
    "!RESERVED!",
    "finger",
    "!RESERVED!",
    "neck",
    "!RESERVED!",
    "body",
    "head",
    "legs",
    "feet",
    "hands",
    "arms",
    "shield",
    "about",
    "waist",
    "wrist",
    "!RESERVED!",
    "!RESERVED!",
    "!RESERVED!",
    "\n"
  };

  if (!arg || !*arg) {
    if (CAN_WEAR(obj, ITEM_WEAR_FINGER))      where = WEAR_FINGER_R;
    if (CAN_WEAR(obj, ITEM_WEAR_NECK))        where = WEAR_NECK_1;
    if (CAN_WEAR(obj, ITEM_WEAR_BODY))        where = WEAR_BODY;
    if (CAN_WEAR(obj, ITEM_WEAR_HEAD))        where = WEAR_HEAD;
    if (CAN_WEAR(obj, ITEM_WEAR_LEGS))        where = WEAR_LEGS;
    if (CAN_WEAR(obj, ITEM_WEAR_FEET))        where = WEAR_FEET;
    if (CAN_WEAR(obj, ITEM_WEAR_HANDS))       where = WEAR_HANDS;
    if (CAN_WEAR(obj, ITEM_WEAR_ARMS))        where = WEAR_ARMS;
    if (CAN_WEAR(obj, ITEM_WEAR_SHIELD))      where = WEAR_SHIELD;
    if (CAN_WEAR(obj, ITEM_WEAR_ABOUT))       where = WEAR_ABOUT;
    if (CAN_WEAR(obj, ITEM_WEAR_WAIST))       where = WEAR_WAIST;
    if (CAN_WEAR(obj, ITEM_WEAR_WRIST))       where = WEAR_WRIST_R;
  } else if ((where = search_block(arg, keywords, FALSE)) < 0)
    send_to_char(ch, "'%s'?  What part of your body is THAT?\r\n", arg);

  return (where);
}

/**
 * WEAR command
 *
 * This command allows a character to wear or equip an object
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_wear)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  itemData_t *obj, *next_obj;
  int where, dotmode, items_worn = 0;

  two_arguments(argument, arg1, arg2);

  if (!*arg1) {
    send_to_char(ch, "Wear what?\r\n");
    return;
  }
  dotmode = find_all_dots(arg1);

  if (*arg2 && (dotmode != FIND_INDIV)) {
    send_to_char(ch, "You can't specify the same body location for more than one item!\r\n");
    return;
  }
  if (dotmode == FIND_ALL) {
    for (obj = ch->carrying; obj; obj = next_obj) {
      next_obj = obj->nextContent;
      if (CAN_SEE_ITEM(ch, obj) && (where = find_eq_pos(ch, obj, 0)) >= 0) {
	items_worn++;
	perform_wear(ch, obj, where);
      }
    }
    if (!items_worn)
      send_to_char(ch, "You don't seem to have anything wearable.\r\n");
  } else if (dotmode == FIND_ALLDOT) {
    if (!*arg1) {
      send_to_char(ch, "Wear all of what?\r\n");
      return;
    }
    if (!(obj = get_item_in_list_vis(ch, arg1, NULL, ch->carrying)))
      send_to_char(ch, "You don't seem to have any %ss.\r\n", arg1);
    else
      while (obj) {
	next_obj = get_item_in_list_vis(ch, arg1, NULL, obj->nextContent);
	if ((where = find_eq_pos(ch, obj, 0)) >= 0)
	  perform_wear(ch, obj, where);
	else
	  act("You can't wear $p.", FALSE, ch, obj, 0, TO_CHAR);
	obj = next_obj;
      }
  } else {
    if (!(obj = get_item_in_list_vis(ch, arg1, NULL, ch->carrying)))
      send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg1), arg1);
    else {
      if ((where = find_eq_pos(ch, obj, arg2)) >= 0)
	perform_wear(ch, obj, where);
      else if (!*arg2)
	act("You can't wear $p.", FALSE, ch, obj, 0, TO_CHAR);
    }
  }
}

/**
 * WIELD command
 *
 * This command allows the user to wield an object as a weapon
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_wield)
{
  char arg[MAX_INPUT_LENGTH];
  itemData_t *obj;

  one_argument(argument, arg);

  if (!*arg)
    send_to_char(ch, "Wield what?\r\n");
  else if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
    send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
  else {
    if (!CAN_WEAR(obj, ITEM_WEAR_WIELD))
      send_to_char(ch, "You can't wield that.\r\n");
    else if (GET_ITEM_WEIGHT(obj) > str_app[STRENGTH_APPLY_INDEX(ch)].wield_w)
      send_to_char(ch, "It's too heavy for you to use.\r\n");
    else
      perform_wear(ch, obj, WEAR_WIELD);
  }
}

/**
 * GRAB and HOLD command
 *
 * This command allows the user to grab/hold an object
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_grab)
{
  char arg[MAX_INPUT_LENGTH];
  itemData_t *obj;

  one_argument(argument, arg);

  if (!*arg)
    send_to_char(ch, "Hold what?\r\n");
  else if (!(obj = get_item_in_list_vis(ch, arg, NULL, ch->carrying)))
    send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
  else {
    if (GET_ITEM_TYPE(obj) == ITEM_LIGHT)
      perform_wear(ch, obj, WEAR_LIGHT);
    else {
      if (!CAN_WEAR(obj, ITEM_WEAR_HOLD) && GET_ITEM_TYPE(obj) != ITEM_WAND &&
      GET_ITEM_TYPE(obj) != ITEM_STAFF && GET_ITEM_TYPE(obj) != ITEM_SCROLL &&
	  GET_ITEM_TYPE(obj) != ITEM_POTION)
	send_to_char(ch, "You can't hold that.\r\n");
      else
	perform_wear(ch, obj, WEAR_HOLD);
    }
  }
}

/**
 * Remove a character's equipped object from a specified equipment position
 *
 * @param ch Character who's equipment is to be removed
 * @param pos Equipment position from which to remove the object
 * @returns none
 */
void perform_remove(charData_t *ch, int pos)
{
  itemData_t *obj;

  if (!(obj = GET_EQ(ch, pos)))
    log("SYSERR: perform_remove: bad pos %d passed.", pos);
  else if (ITEM_FLAGGED(obj, ITEM_NODROP))
    act("You can't remove $p, it must be CURSED!", FALSE, ch, obj, 0, TO_CHAR);
  else if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch))
    act("$p: you can't carry that many items!", FALSE, ch, obj, 0, TO_CHAR);
  else {
    itemData_toChar(char_unequipItem(ch, pos), ch);
    act("You stop using $p.", FALSE, ch, obj, 0, TO_CHAR);
    act("$n stops using $p.", TRUE, ch, obj, 0, TO_ROOM);
  }
}

/**
 * REMOVE command
 *
 * This command lets a character remove equipped object(s)
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_remove)
{
  char arg[MAX_INPUT_LENGTH];
  int i, dotmode, found;

  one_argument(argument, arg);

  if (!*arg) {
    send_to_char(ch, "Remove what?\r\n");
    return;
  }
  dotmode = find_all_dots(arg);

  if (dotmode == FIND_ALL) {
    found = 0;
    for (i = 0; i < NUM_WEARS; i++)
      if (GET_EQ(ch, i)) {
	perform_remove(ch, i);
	found = 1;
      }
    if (!found)
      send_to_char(ch, "You're not using anything.\r\n");
  } else if (dotmode == FIND_ALLDOT) {
    if (!*arg)
      send_to_char(ch, "Remove all of what?\r\n");
    else {
      found = 0;
      for (i = 0; i < NUM_WEARS; i++)
	if (GET_EQ(ch, i) && CAN_SEE_ITEM(ch, GET_EQ(ch, i)) &&
	    isname(arg, GET_EQ(ch, i)->name)) {
	  perform_remove(ch, i);
	  found = 1;
	}
      if (!found)
	send_to_char(ch, "You don't seem to be using any %ss.\r\n", arg);
    }
  } else {
    if ((i = get_item_pos_in_equip_vis(ch, arg, NULL, ch->equipment)) < 0)
      send_to_char(ch, "You don't seem to be using %s %s.\r\n", AN(arg), arg);
    else
      perform_remove(ch, i);
  }
}