nakedmudv3.0/
nakedmudv3.0/lib/
nakedmudv3.0/lib/logs/
nakedmudv3.0/lib/misc/
nakedmudv3.0/lib/players/
nakedmudv3.0/lib/pymodules/
nakedmudv3.0/lib/txt/
nakedmudv3.0/lib/world/
nakedmudv3.0/lib/world/examples/
nakedmudv3.0/lib/world/examples/mproto/
nakedmudv3.0/lib/world/examples/oproto/
nakedmudv3.0/lib/world/examples/reset/
nakedmudv3.0/lib/world/examples/rproto/
nakedmudv3.0/lib/world/examples/trigger/
nakedmudv3.0/lib/world/limbo/
nakedmudv3.0/lib/world/limbo/rproto/
nakedmudv3.0/src/alias/
nakedmudv3.0/src/char_vars/
nakedmudv3.0/src/editor/
nakedmudv3.0/src/example_module/
nakedmudv3.0/src/help/
nakedmudv3.0/src/set_val/
nakedmudv3.0/src/socials/
nakedmudv3.0/src/time/
//*****************************************************************************
//
// cmd_manip.c
//
// a set of commands that NakedMud(tm) comes with that allows characters to
// manipulate various things. These commands are mostly directed towards
// manipulating objects (e.g. get, put, drop, etc...) but can also affect other
// things like exits (e.g. open, close)
//
//*****************************************************************************
#include "mud.h"
#include "utils.h"
#include "handler.h"
#include "inform.h"
#include "character.h"
#include "exit.h"
#include "world.h"
#include "room.h"
#include "object.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "items/items.h"
#include "items/container.h"



//*****************************************************************************
// local functions
//*****************************************************************************

//
// used by open, close, lock, and unlock. When an exit is manipulated on one
// side, it is the case that we'll want to do an identical manipulation on the
// other side. That's what we do here. Note: Can only do close OR lock with
// one call to this function. Cannot handle both at the same time!
void try_manip_other_exit(ROOM_DATA *room, EXIT_DATA *exit,
			  bool closed, bool locked) {
  // see if there's a room on the other side
  ROOM_DATA         *to = worldGetRoom(gameworld, exitGetTo(exit));
  EXIT_DATA *other_exit = NULL;
  const char   *opp_dir = NULL;
  if(to == NULL)
    return;

  // check to see if we can figure out a return direction
  if(*exitGetOpposite(exit))
    opp_dir = exitGetOpposite(exit);
  else {
    int opp_num = dirGetNum(roomGetExitDir(room, exit));
    if(opp_num != DIR_NONE)
      opp_dir = dirGetName(dirGetOpposite(opp_num));
  }

  // do we have an opposite direction to manipulate?
  if(opp_dir == NULL)
    return;

  // do we have an opposite exit to manipulate?
  if( (other_exit = roomGetExit(to, opp_dir)) != NULL) {
    // are we changing the close state, and the exit's not locked?
    if(exitIsClosed(other_exit) != closed && !exitIsLocked(other_exit)) {
      exitSetClosed(other_exit, closed);
      send_to_list(roomGetCharacters(to),
		   "%s %s from the other side.\r\n",
		   (*exitGetName(other_exit)?exitGetName(other_exit):
		    "An exit"),
		   (closed ? "closes" : "opens"));
    }

    // are we changing the lock state, and the exit is closed?
    if(exitIsLocked(other_exit) != locked && exitIsClosed(other_exit)) {
      exitSetLocked(other_exit, locked);
      send_to_list(roomGetCharacters(to),
		   "%s %s from the other side.\r\n",
		   (*exitGetName(other_exit)?exitGetName(other_exit):
		    "An exit"),
		   (locked ? "locks" : "unlocks"));
    }
  }
}



//*****************************************************************************
// player commands
//*****************************************************************************


//
// try to lock an exit or container. The container can be anything in our
// immediate visible range (room, inventory, body). do_lock automatically
// checks if we have the key on us.
//
//  examples:
//    lock door                lock a door in the room
//    lock south               lock the south exit
//    lock 2.chest             lock the 2nd chest in our visible range
COMMAND(cmd_lock) {
  int found_type = PARSE_NONE;
  void    *found = NULL;

  if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv.eq exit }", 
		 &found, &found_type))
    return;

  // did we find an exit or an object?
  if(found_type == PARSE_EXIT) {
    if(!exitIsClosed(found))
      send_to_char(ch, "%s must be closed first.\r\n", exitGetName(found));
    else if(exitIsLocked(found))
      send_to_char(ch, "%s is already locked.\r\n", exitGetName(found));
    else if(!*exitGetKey(found))
      send_to_char(ch, "You cannot figure out how %s would be locked.\r\n",
		   exitGetName(found));
    else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), 
			     get_key_locale(roomGetClass(charGetRoom(ch))))))
      send_to_char(ch, "You cannot seem to find the key.\r\n");
    else {
      send_to_char(ch, "You lock %s.\r\n", exitGetName(found));
      send_around_char(ch, TRUE, "%s locks %s.\r\n", 
		       charGetName(ch), exitGetName(found));
      exitSetLocked(found, TRUE);

      // and try the other side
      try_manip_other_exit(charGetRoom(ch), found, exitIsClosed(found),
			   TRUE);
    }
  }

  // object found
  else { // if(found_type == PARSE_OBJ) {
    if(!objIsType(found, "container"))
      send_to_char(ch, "%s is not a container.\r\n", objGetName(found));
    else if(!containerIsClosed(found))
      send_to_char(ch, "%s must be closed first.\r\n", objGetName(found));
    else if(containerIsLocked(found))
      send_to_char(ch, "%s is already locked.\r\n", objGetName(found));
    else if(!*containerGetKey(found))
      send_to_char(ch, "You cannot figure out how %s would be locked.\r\n",
		   objGetName(found));
    else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found),
				     get_key_locale(objGetClass(found)))))
      send_to_char(ch, "You cannot seem to find the key.\r\n");
    else {
      send_to_char(ch, "You lock %s.\r\n", objGetName(found));
      message(ch, NULL, found, NULL, TRUE, TO_ROOM, "$n locks $o.");
      containerSetLocked(found, TRUE);
    }
  }
}


//
// the opposite of lock
COMMAND(cmd_unlock) {
  int found_type = PARSE_NONE;
  void    *found = NULL;

  if(!parse_args(ch, TRUE, cmd, arg, "[the] { obj.room.inv exit }", 
		 &found, &found_type))
    return;

  // did we find something?
  if(found_type == PARSE_EXIT) {
    if(!exitIsLocked(found))
      send_to_char(ch, "%s is not locked.\r\n", exitGetName(found));
    else if(!*exitGetKey(found))
      send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n",
		   exitGetName(found));
    else if(!has_obj(ch, get_fullkey_relative(exitGetKey(found), 
			     get_key_locale(roomGetClass(charGetRoom(ch))))))
      send_to_char(ch, "You cannot seem to find the key.\r\n");
    else {
      send_to_char(ch, "You unlock %s.\r\n", exitGetName(found));
      send_around_char(ch, TRUE, "%s unlocks %s.\r\n", 
		       charGetName(ch), exitGetName(found));
      exitSetLocked(found, FALSE);

      // and try the other side
      try_manip_other_exit(charGetRoom(ch), found, exitIsClosed(found),
			   FALSE);
    }
  }

  else { // if(found_type == PARSE_OBJ) {
    if(!objIsType(found, "container"))
      send_to_char(ch, "%s is not a container.\r\n", objGetName(found));
    else if(!containerIsLocked(found))
      send_to_char(ch, "%s is not locked.\r\n", objGetName(found));
    else if(!*containerGetKey(found))
      send_to_char(ch, "You cannot figure out how %s would be unlocked.\r\n",
		   objGetName(found));
    else if(!has_obj(ch, get_fullkey_relative(containerGetKey(found), 
				     get_key_locale(objGetClass(found)))))
      send_to_char(ch, "You cannot seem to find the key.\r\n");
    else {
      send_to_char(ch, "You unlock %s.\r\n", objGetName(found));
      message(ch, NULL, found, NULL, TRUE, TO_ROOM, "$n unlocks $o.");
      containerSetLocked(found, FALSE);
    }
  }
}


//
//  put one thing into another. The thing you wish to put must be in
//  your inventory. The container must be in your immediate visible range
//  (room, inventory, body)
//
//  usage: put [the] <thing> [in the] <container>
//
//  examples:
//    put coin bag             put a coin into the bag
//    put all.shirt closet     put all of the shirts in the closet
COMMAND(cmd_put) {
  void    *found = NULL;
  bool  multiple = FALSE;
  OBJ_DATA *cont = NULL;

  if(!parse_args(ch, TRUE, cmd, arg, 
		 "[the] obj.inv.multiple [in the] obj.room.inv",
		 &found, &multiple, &cont))
    return;
  
  // make sure we have a container
  if(!objIsType(cont, "container"))
    send_to_char(ch, "%s is not a container.\r\n", objGetName(cont));

  // did we find a list of things or a single item?
  else if(multiple == FALSE)
    do_put(ch, found, cont);

  // we have to move a bunch of things
  else {
    OBJ_DATA *obj = NULL;
    while( (obj = listPop(found)) != NULL)
      do_put(ch, obj, cont);
    deleteList(found);
  }
}


//
//  attempt to open a door or container. The container must be in our immediate
//  visible range (room, inventory, body).
//
//  usage: open [the] <thing>
//
//  examples:
//    open door               open a door
//    open 2.bag              open your second bag
//    open east               open the east exit
//    open backpack on self   open a backpack you are wearing
COMMAND(cmd_open) {
  void    *found = NULL;
  int found_type = PARSE_NONE;

  if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.inv.eq exit }",
		 &found, &found_type))
    return;

  // open an exit
  if(found_type == PARSE_EXIT) {
    if(!exitIsClosable(found))
      send_to_char(ch, "But %s cannot be opened!\r\n",
		   (*exitGetName(found) ? exitGetName(found) : "it"));
    else if(!exitIsClosed(found))
      send_to_char(ch, "It is it easy to open something when someone "
		   "has already done it for you!\r\n");
    else if(exitIsLocked(found))
      send_to_char(ch, "%s appears to be locked.\r\n",
		   (*exitGetName(found) ? exitGetName(found) : "It"));
    else {
      mssgprintf(ch, NULL, NULL, NULL, FALSE, TO_ROOM, "$n opens %s.", 
		 (*exitGetName(found) ? exitGetName(found) : "an exit"));
      send_to_char(ch, "You open %s.\r\n",
		   (*exitGetName(found) ? exitGetName(found) : "the exit"));
      exitSetClosed(found, FALSE);

      // try opening the other side
      try_manip_other_exit(charGetRoom(ch), found, FALSE, exitIsLocked(found));
    }
  }

  // open a container
  else { // if(found_type == FOUND_OBJ) {
    // make sure it's a container and it can be opened
    if(!objIsType(found, "container") || !containerIsClosable(found))
      send_to_char(ch, "But it cannot be opened!\r\n");
    else if(!containerIsClosed(found))
      send_to_char(ch, "It is already opened.\r\n");
    else if(containerIsLocked(found))
      send_to_char(ch, "It appears to be locked.\r\n");
    else {
      send_to_char(ch, "You open %s.\r\n", objGetName(found));
      message(ch, NULL, found, NULL, FALSE, TO_ROOM, "$n opens $o.");
      containerSetClosed(found, FALSE);
    }
  }
}


//
// cmd_close is used to close containers and exits.
//   usage: open <thing>
//
//   examples:
//     close door               close a door
//     close 2.bag              close your second bag
//     close east               close the east exit
//     close backpack on self   close a backpack you are wearing
COMMAND(cmd_close) {
  void    *found = NULL;
  int found_type = PARSE_NONE;

  if(!parse_args(ch, TRUE, cmd, arg, "{ obj.room.eq.inv exit }",
		 &found, &found_type))
    return;

  // close an exit
  if(found_type == PARSE_EXIT) {
    if(!exitIsClosable(found))
      send_to_char(ch, "But %s cannot be closed!\r\n",
		   (*exitGetName(found) ? exitGetName(found) : "it"));
    else if(exitIsClosed(found))
      send_to_char(ch, "It is easy to close something when someone "
		   "has already done it for you!\r\n");
    else {
      char other_buf[SMALL_BUFFER];
      sprintf(other_buf, "$n closes %s.", (*exitGetName(found) ?
					   exitGetName(found) : "an exit"));
      message(ch, NULL, NULL, NULL, FALSE, TO_ROOM, other_buf);
      send_to_char(ch, "You close %s.\r\n",
		   (*exitGetName(found) ? exitGetName(found) : "the exit"));
      exitSetClosed(found, TRUE);

      // try opening the other side
      try_manip_other_exit(charGetRoom(ch), found, TRUE, exitIsLocked(found));
    }
  }

  // close a container
  else { // if(found_type == PARSE_OBJ) {
    // make sure it's a container and it can be closed
    if(!objIsType(found, "container") || !containerIsClosable(found))
      send_to_char(ch, "But it cannot even be closed!\r\n");
    else if(containerIsClosed(found))
      send_to_char(ch, "It is already closed.\r\n");
    else {
      send_to_char(ch, "You close %s.\r\n", objGetName(found));
      message(ch, NULL, found, NULL, FALSE, TO_ROOM, "$n closes $o.");
      containerSetClosed(found, TRUE);
    }
  }
}


//
// cmd_get is used to move objects from containers or the room to your inventory
//   usage: get <object> <from>
//
//   examples:
//     get sword            get a sword from the room
//     get 2.cupcake bag    get the second cupcake from your bag
//     get all.coin         get all of the coins on the ground
COMMAND(cmd_get) {
  if(!arg || !*arg) {
    send_to_char(ch, "What did you want to get?\r\n");
    return;
  }

  // the name of what we're trying to get
  char name[SMALL_BUFFER];
  arg = one_arg(arg, name);

  // first check to see if we're trying to get from a container
  OBJ_DATA *cont = NULL;
  if(*arg) {
    cont = generic_find(ch, arg, FIND_TYPE_OBJ, FIND_SCOPE_IMMEDIATE,
			FALSE, NULL);
    // were we trying to get something from a container 
    // but couldn't find the container?
    if(cont == NULL) {
      send_to_char(ch, "Get what from what?\r\n");
      return;
    }
    else if(!objIsType(cont, "container")) {
      send_to_char(ch, "%s is not a container.\r\n", objGetName(cont));
      return;
    }
    else if(containerIsClosed(cont)) {
      send_to_char(ch, "%s is closed. Try opening it first.\r\n", 
		   objGetName(cont));
      return;
    }
  }

  int found_type = FOUND_NONE;
  void    *found = NULL;

  // if we have a container, just search the container for things
  if(cont != NULL) {
    // oi... this is going to get messy. We have to do some stuff
    // that the generic_find will usually do for us - i.e. checking
    // to see if we need to get a list of items or a single item
    // we should really add a new function in the handler for doing this
    int count = 1;
    get_count(name, name, &count);
    if(count == COUNT_ALL) {
      found_type = FOUND_LIST;
      found      = find_all_objs(ch, objGetContents(cont), name, NULL, TRUE);
    }
    else {
      found_type = FOUND_OBJ;
      found      = find_obj(ch, objGetContents(cont), count, name, NULL, TRUE);
    }
  }
  // otherwise, search the room for visible things
  else
    found = generic_find(ch, name, 
			 FIND_TYPE_OBJ, 
			 FIND_SCOPE_ROOM | FIND_SCOPE_VISIBLE, 
			 TRUE, &found_type);


  if(found && found_type == FOUND_OBJ)
    do_get(ch, found, cont);
  else if(found && found_type == FOUND_LIST) {
    OBJ_DATA *obj = NULL;
    while( (obj = listPop(found)) != NULL)
      do_get(ch, obj, cont);
    deleteList(found);
  }
  else
    send_to_char(ch, "You can't find what you're looking for.\r\n");
}


//
// cmd_give is used to transfer an object in your possession to 
// another character
//   usage: give [the] <object> [to] <person>
//
//   examples:
//     give doll girl           give a doll in your inventory to a girl
//     give all.coin robber     give all of your money to the robber
//
COMMAND(cmd_give) {
  CHAR_DATA *recv = NULL;  // the person we're giving stuff to
  void   *to_give = NULL;  // may be a list or a single item
  bool   multiple = FALSE; // are we giving one or multiple items?

  // try to give objects from our inventory. We can give multiple items. Give
  // them to a person in the room who is not ourself. The fact we can see the
  // receiver is implied. If we fail to find our items or receiver, parse_args
  // will tell the character what he did wrong, and we will halt the command
  if(!parse_args(ch,TRUE,cmd,arg, "[the] obj.inv.multiple [to] ch.room.noself",
		 &to_give, &multiple, &recv))
    return;

  // just a single item to give...
  if(multiple == FALSE)
    do_give(ch, recv, to_give);
  // we have a list of items to give
  else {
    LIST_ITERATOR *obj_i = newListIterator(to_give);
    OBJ_DATA        *obj = NULL;
    ITERATE_LIST(obj, obj_i) {
      do_give(ch, recv, obj);
    } deleteListIterator(obj_i);

    // we also have to delete the list that parse_args sent us
    deleteList(to_give);
  }
}


//
// cmd_drop is used to transfer an object in your inventory to the ground
//   usage: drop <item>
//
//   examples:
//     drop bag          drop a bag you have
//     drop all.bread    drop all of the bread you are carrying
//     drop 2.cupcake    drop the second cupcake in your posession
COMMAND(cmd_drop) {
  void   *found = NULL;
  bool multiple = FALSE;

  if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple",&found,&multiple))
    return;

  // are we dropping a list of things, or just one?
  if(multiple == FALSE)
    do_drop(ch, found);

  // we got a list of things... drop 'em all
  else {
    OBJ_DATA *obj = NULL;
    while( (obj = listPop(found)) != NULL)
      do_drop(ch, obj);
    deleteList(found);
  }
}


//
// cmd_wear is used to equip wearable items in your inventory to your body
//   usage: wear [object] [where]
//
//   examples:
//     wear shirt                            equip a shirt
//     wear all.ring                         wear all of the rings in your 
//                                           inventory
//     wear gloves left hand, right hand     wear the gloves on your left and
//                                           right hands
COMMAND(cmd_wear) {
  void   *found = NULL;
  bool multiple = FALSE;
  char   *where = NULL;

  if(!parse_args(ch, TRUE, cmd, arg, "[the] obj.inv.multiple | [on my] string", 
		 &found, &multiple, &where))
    return;

  // are we wearing one thing, or multiple things?
  if(multiple == FALSE)
    do_wear(ch, found, where);

  // we're trying to wear multiple items
  else {
    OBJ_DATA *obj = NULL;
    while( (obj = listPop(found)) != NULL)
      do_wear(ch, obj, where);
    deleteList(found);
  }
}


//
// cmd_remove is used to unequip items on your body to your inventory
//   usage: remove <item>
//
//   examples:
//     remove mask             remove the mask you are wearing
//     remove all.ring         remove all the rings you have on
//     remove 2.ring           remove the 2nd ring you have equipped
COMMAND(cmd_remove) {
  void   *found = NULL;
  bool multiple = FALSE;

  if(!parse_args(ch, TRUE, cmd, arg, "obj.eq.multiple", &found, &multiple))
    return;

  // are we trying to remove one thing, or multiple things?
  if(multiple == FALSE)
    do_remove(ch, found);

  // removing multiple things...
  else {
    OBJ_DATA *obj = NULL;
    while( (obj = listPop(found)) != NULL)
      do_remove(ch, obj);
    deleteList(found);
  }
}