nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// handler.c
//
// this file contains implementations for all of the "handling" functions;
// functions that move things from place to place. e.g. adding and removing
// characters from rooms, as well as objects and the like.
//
//*****************************************************************************
#include "mud.h"
#include "utils.h"
#include "world.h"
#include "room.h"
#include "exit.h"
#include "extra_descs.h"
#include "character.h"
#include "object.h"
#include "body.h"
#include "inform.h"
#include "hooks.h"
#include "handler.h"
#include "commands.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "items/items.h"
#include "items/container.h"
#include "items/worn.h"
#include "scripts/scripts.h"
#include "scripts/pymud.h"



//*****************************************************************************
// obj/char from/to functions
//*****************************************************************************
void exit_exist(EXIT_DATA *exit) {
  propertyTablePut(exit_table, exit);
}

bool exit_exists(EXIT_DATA *exit) {
  return propertyTableIn(exit_table, exitGetUID(exit));
}

void exit_to_game(EXIT_DATA *exit) {
  if(!exit_exists(exit))
    exit_exist(exit);
}

void obj_exist(OBJ_DATA *obj) {
  propertyTablePut(obj_table, obj);

  // also add all contents
  if(listSize(objGetContents(obj)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
    OBJ_DATA *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_exist(cont);
    deleteListIterator(cont_i);
  }
}

bool obj_exists(OBJ_DATA *obj) {
  return propertyTableIn(obj_table, objGetUID(obj));
}

void obj_to_game(OBJ_DATA *obj) {
  if(!obj_exists(obj))
    obj_exist(obj);
  listPut(object_list, obj);

  // execute all of our to_game hooks
  hookRun("obj_to_game", hookBuildInfo("obj", obj));

  // also add all contents
  if(listSize(objGetContents(obj)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
    OBJ_DATA *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_to_game(cont);
    deleteListIterator(cont_i);
  }
}

void room_exist(ROOM_DATA *room) {
  propertyTablePut(room_table, room);

  // add contents
  if(listSize(roomGetContents(room)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(roomGetContents(room));
    OBJ_DATA        *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_exist(cont);
    deleteListIterator(cont_i);
  }

  // add its people
  if(listSize(roomGetCharacters(room)) > 0) {
    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
    CHAR_DATA       *ch = NULL;
    ITERATE_LIST(ch, ch_i)
      char_exist(ch);
    deleteListIterator(ch_i);
  }

  // add its exits
  LIST       *ex_list = roomGetExitNames(room);
  LIST_ITERATOR *ex_i = newListIterator(ex_list);
  char           *dir = NULL;
  ITERATE_LIST(dir, ex_i) {
    exit_exist(roomGetExit(room, dir));
  } deleteListIterator(ex_i);
  deleteListWith(ex_list, free);
}

bool room_exists(ROOM_DATA *room) {
  return propertyTableIn(room_table, roomGetUID(room));
}

void room_to_game(ROOM_DATA *room) {
  if(!room_exists(room))
    room_exist(room);
  listPut(room_list, room);

  // execute all of our to_game hooks
  hookRun("room_to_game", hookBuildInfo("rm", room));

  // add contents
  if(listSize(roomGetContents(room)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(roomGetContents(room));
    OBJ_DATA        *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_to_game(cont);
    deleteListIterator(cont_i);
  }

  // add its people
  if(listSize(roomGetCharacters(room)) > 0) {
    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
    CHAR_DATA       *ch = NULL;
    ITERATE_LIST(ch, ch_i)
      char_to_game(ch);
    deleteListIterator(ch_i);
  }

  // add its exits, and their room table commands as neccessary
  LIST       *ex_list = roomGetExitNames(room);
  LIST_ITERATOR *ex_i = newListIterator(ex_list);
  char           *dir = NULL;
  ITERATE_LIST(dir, ex_i) {
    exit_to_game(roomGetExit(room, dir));
    if(get_cmd_move() != NULL && dirGetNum(dir) == DIR_NONE)
      nearMapPut(roomGetCmdTable(room), dir, NULL,
		 newPyCmd(dir, get_cmd_move(), POS_STANDING, POS_FLYING,
			"player", TRUE, TRUE));
  } deleteListIterator(ex_i);
  deleteListWith(ex_list, free);
}

void char_exist(CHAR_DATA *ch) {
  propertyTablePut(mob_table, ch);

  // also add inventory
  if(listSize(charGetInventory(ch)) > 0) {
    LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, inv_i)
      obj_exist(obj);
    deleteListIterator(inv_i);
  }

  // and equipped items
  LIST *eq = bodyGetAllEq(charGetBody(ch));
  if(listSize(eq) > 0) {
    LIST_ITERATOR *eq_i = newListIterator(eq);
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, eq_i)
      obj_exist(obj);
    deleteListIterator(eq_i);
  }
  deleteList(eq);
}

bool char_exists(CHAR_DATA *ch) {
  return propertyTableIn(mob_table, charGetUID(ch));
}

void char_to_game(CHAR_DATA *ch) {
  if(!char_exists(ch))
    char_exist(ch);
  listPut(mobile_list, ch);

  // execute all of our to_game hooks
  hookRun("char_to_game", hookBuildInfo("ch", ch));

  // also add inventory
  if(listSize(charGetInventory(ch)) > 0) {
    LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, inv_i)
      obj_to_game(obj);
    deleteListIterator(inv_i);
  }

  // and equipped items
  LIST *eq = bodyGetAllEq(charGetBody(ch));
  if(listSize(eq) > 0) {
    LIST_ITERATOR *eq_i = newListIterator(eq);
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, eq_i)
      obj_to_game(obj);
    deleteListIterator(eq_i);
  }
  deleteList(eq);
}

void exit_from_game(EXIT_DATA *exit) {
  propertyTableRemove(exit_table, exitGetUID(exit));
}

void obj_from_game(OBJ_DATA *obj) {
  // go through all of our fromgame hooks
  hookRun("obj_from_game", hookBuildInfo("obj", obj));

  // also remove everything that is contained within the object
  if(listSize(objGetContents(obj)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(objGetContents(obj));
    OBJ_DATA *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_from_game(cont);
    deleteListIterator(cont_i);
  }

  listRemove(object_list, obj);
  propertyTableRemove(obj_table, objGetUID(obj));
}

void room_from_game(ROOM_DATA *room) {
  // go through all of our fromgame hooks
  hookRun("room_from_game", hookBuildInfo("rm", room));

  // also remove all the objects contained within the room
  if(listSize(roomGetContents(room)) > 0) {
    LIST_ITERATOR *cont_i = newListIterator(roomGetContents(room));
    OBJ_DATA        *cont = NULL;
    ITERATE_LIST(cont, cont_i)
      obj_from_game(cont);
    deleteListIterator(cont_i);
  }

  // and now all of the characters
  if(listSize(roomGetCharacters(room)) > 0) {
    LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(room));
    CHAR_DATA       *ch = NULL;
    ITERATE_LIST(ch, ch_i)
      char_from_game(ch);
    deleteListIterator(ch_i);
  }

  // remove its exits
  LIST       *ex_list = roomGetExitNames(room);
  LIST_ITERATOR *ex_i = newListIterator(ex_list);
  char           *dir = NULL;
  ITERATE_LIST(dir, ex_i)
    exit_from_game(roomGetExit(room, dir));
  deleteListIterator(ex_i);
  deleteListWith(ex_list, free);

  listRemove(room_list, room);
  propertyTableRemove(room_table, roomGetUID(room));
}

void char_from_game(CHAR_DATA *ch) {
  // go through all of our fromgame hooks
  hookRun("char_from_game", hookBuildInfo("ch", ch));

  // also remove inventory
  if(listSize(charGetInventory(ch)) > 0) {
    LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, inv_i)
      obj_from_game(obj);
    deleteListIterator(inv_i);
  }

  // and equipped items
  LIST *eq = bodyGetAllEq(charGetBody(ch));
  if(listSize(eq) > 0) {
    LIST_ITERATOR *eq_i = newListIterator(eq);
    OBJ_DATA *obj = NULL;
    ITERATE_LIST(obj, eq_i)
      obj_from_game(obj);
    deleteListIterator(eq_i);
  }
  deleteList(eq);

  listRemove(mobile_list, ch);
  propertyTableRemove(mob_table, charGetUID(ch));
}

void obj_from_char(OBJ_DATA *obj) {
  if(objGetCarrier(obj)) {
    listRemove(charGetInventory(objGetCarrier(obj)), obj);
    objSetCarrier(obj, NULL);
  }
}

void obj_from_obj(OBJ_DATA *obj) {
  if(objGetContainer(obj)) {
    listRemove(objGetContents(objGetContainer(obj)), obj);
    objSetContainer(obj, NULL);
  }
}

void obj_from_room(OBJ_DATA *obj) {
  if(objGetRoom(obj)) {
    listRemove(roomGetContents(objGetRoom(obj)), obj);
    objSetRoom(obj, NULL);
  }
}

void obj_to_char(OBJ_DATA *obj, CHAR_DATA *ch) {
  listPut(charGetInventory(ch), obj);
  objSetCarrier(obj, ch);
}

void obj_to_obj(OBJ_DATA *obj, OBJ_DATA *to) {
  listPut(objGetContents(to), obj);
  objSetContainer(obj, to);
}

void obj_to_room(OBJ_DATA *obj, ROOM_DATA *room) {
  listPut(roomGetContents(room), obj);
  objSetRoom(obj, room);
}

void char_from_room(CHAR_DATA *ch) {
  charSetLastRoom(ch, charGetRoom(ch));
  roomRemoveChar(charGetRoom(ch), ch);
  charSetRoom(ch, NULL);
}

void char_to_room(CHAR_DATA *ch, ROOM_DATA *room) {
  if(charGetRoom(ch))
    char_from_room(ch);

  roomAddChar(room, ch);
  charSetRoom(ch, room);
}

void char_from_furniture(CHAR_DATA *ch) {
  objRemoveChar(charGetFurniture(ch), ch);
  charSetFurniture(ch, NULL);
}

void char_to_furniture(CHAR_DATA *ch, OBJ_DATA *furniture) {
  if(charGetFurniture(ch))
    char_from_furniture(ch);

  objAddChar(furniture, ch);
  charSetFurniture(ch, furniture);
}



//*****************************************************************************
// functions related to equipping and unequipping items
//*****************************************************************************
bool try_equip(CHAR_DATA *ch, OBJ_DATA *obj, const char *wanted_pos,
	       const char *required_pos) {
  bool success = FALSE;

  // if we don't need any specific places, try equipping to our wanted spots
  if(!required_pos || !*required_pos)
    success = bodyEquipPosnames(charGetBody(ch), obj, wanted_pos);

  // if we don't want any specific places, equip to whatever is open
  else if(!wanted_pos || !*wanted_pos)
    success = bodyEquipPostypes(charGetBody(ch), obj, required_pos);

  // otherwise, see if the places we want to equip to match what we need,
  // and also make sure we're not trying to equip the same position twice
  else if(!dup_keywords_exist(wanted_pos)) {
    // build lists of what we want and what we need, and compare
    char *want_type_list = list_postypes(charGetBody(ch), wanted_pos);
    LIST     *want_types = parse_keywords(want_type_list); 
    LIST     *need_types = parse_keywords(required_pos);
    bool           match = TRUE;

    // make sure we have both wanted and needed positions
    // and then try to match it all up
    if(listSize(want_types) != listSize(need_types))
      match = FALSE;
    else {
      LIST_ITERATOR *need_i = newListIterator(need_types);
      char        *one_need = NULL;

      // now, make sure that each our our needed positions is represented
      ITERATE_LIST(one_need, need_i) {
	char *found = listRemoveWith(want_types, one_need, strcasecmp);
	// if we found it, free the memory. Otherwise, break out and fail
	if(found == NULL)
	  break;
	else
	  free(found);
      } deleteListIterator(need_i);

      // make sure we accounted for all of our needed positions
      match = (listSize(want_types) == 0);
    }

    // garbage collection
    if(want_type_list) free(want_type_list);
    deleteListWith(want_types, free);
    deleteListWith(need_types, free);

    // if we didn't run into problems, try equipping
    if(match == TRUE)
      success = bodyEquipPosnames(charGetBody(ch), obj, wanted_pos);
  }

  if(success == TRUE)
    objSetWearer(obj, ch);
  return success;
}

bool try_unequip(CHAR_DATA *ch, OBJ_DATA *obj) {
  if(bodyUnequip(charGetBody(ch), obj)) {
    objSetWearer(obj, NULL);
    return TRUE;
  }
  return FALSE;
}

//
// unequip everything the character is wearing, and put it to his or her inv
//
void unequip_all(CHAR_DATA *ch) {
  LIST      *eq = bodyGetAllEq(charGetBody(ch));
  OBJ_DATA *obj = NULL;
  while( (obj = listPop(eq)) != NULL) {
    if(bodyUnequip(charGetBody(ch), obj)) {
      objSetWearer(obj, NULL);
      obj_to_char(obj, ch);
    }
  } deleteList(eq);
}



//*****************************************************************************
//
// Functions related to generic_find() and find_specific()
//
//*****************************************************************************

//
// finds the first argument after "arg". return NULL if nothing is found.
// the returned string must be freed afterwards
//
char *after_arg(char *target, char *arg) {
  int len   = strlen(arg);
  int t_len = strlen(target);
  int i = 0;

  // loop through and find our target
  for(i = 0; i < len - t_len - 1; i++) {
    // we've found the target, and there's a space after it
    if(!strncasecmp(arg+i, target, t_len) && isspace(arg[i+t_len])) {
      // skip up to past the next space(s)
      i = i + t_len + 1;
      while(isspace(arg[i])) 
	i++;

      // find where the next space or string terminator is
      int j = i+1;
      while(!(isspace(arg[j]) || arg[j] == '\0'))
	j++;

      // make a buffer to hold the word following our target
      char buf[j-i+1];
      strncpy(buf, arg+i, j-i);
      buf[j-i] = '\0';
      return strdup(buf);
    }
  }

  return NULL;
}


//
// finds the first argument after "at". return NULL if nothing is found.
// the returned string must be freed afterwards
//
char *at_arg(char *arg) {
  return after_arg("at", arg);
}


//
// finds the first argument after "on". return NULL if nothing is found.
// the returned string must be freed afterwards
//
char *on_arg(char *arg) {
  return after_arg("on", arg);
}


//
// finds the first argument after "in". return NULL if nothing is found.
// the returned string must be freed afterwards
//
char *in_arg(char *arg) {
  return after_arg("in", arg);
}


//
// Can find: objects and extra descriptions
//
void *find_on_char(CHAR_DATA *looker,
		   CHAR_DATA *on,
		   int at_count, const char *at,
		   bitvector_t find_types,
		   bitvector_t find_scope,
		   int *found_type) {
  int count = 0;

  // see if it's equipment
  if(IS_SET(find_types, FIND_TYPE_OBJ)) {
    LIST *equipment = bodyGetAllEq(charGetBody(on));
    count += count_objs(looker, equipment, at, NULL,
			(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NULL, 
			       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      deleteList(equipment);
      return obj;
    }
    else
      deleteList(equipment);
  }

  // see if it's an extra description
  //***********
  // FINISH ME
  //***********

  if(found_type)
    *found_type = FOUND_NONE;
  return NULL;
}


//
// Can find: extra descriptions, chars
//
void *find_on_obj(CHAR_DATA *looker,
		  OBJ_DATA  *on,
		  int at_count, const char *at,
		  bitvector_t find_types,
		  bitvector_t find_scope,
		  int *found_type) {
  int count = 0;

  // see if it's a character
  if(IS_SET(find_types, FIND_TYPE_CHAR)) {
    count = count_chars(looker, objGetUsers(on), at, NULL,
			(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_CHAR;
      return find_char(looker, objGetUsers(on), at_count, at, NULL,
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
      at_count -= count;
  }

  // see if it's an extra description
  if(IS_SET(find_types, FIND_TYPE_EDESC)) {
    count = (objGetEdesc(on, at) != NULL);
    if(count && at_count == 1) {
      if(found_type)
	*found_type = FOUND_EDESC;
      return edescSetGet(objGetEdescs(on), at);
    }
    else
      at_count--;
  }
 
  if(found_type)
    *found_type = FOUND_NONE;
  return NULL;
}


//
// Can find: objects and extra descriptions
//
void *find_in_obj(CHAR_DATA *looker,
		  OBJ_DATA *in,
		  int at_count, const char *at,
		  int on_count, const char *on,
		  bitvector_t find_types,
		  bitvector_t find_scope,
		  int *found_type) {		  
  if(found_type)
    *found_type = FOUND_NONE;

  // see if we're looking on anything
  if(on && *on && on_count > 0) {
    OBJ_DATA *on_obj = find_obj(looker, objGetContents(in), on_count, on, NULL,
				(IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(!on_obj)
      return NULL;
    else
      return find_on_obj(looker, on_obj, at_count, at, 
			 find_types, find_scope, found_type);
  }
  else {
    int count = count_objs(looker, objGetContents(in), at, NULL,
			    (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      return find_obj(looker, objGetContents(in), at_count, at, NULL,
		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
      return NULL;
  }
}


LIST *find_all(CHAR_DATA *looker, const char *at, bitvector_t find_types,
	       bitvector_t find_scope, int *found_type) {
  if(found_type)
    *found_type = FOUND_LIST;

  /************************************************************/
  /*                        FIND ALL OBJS                     */
  /************************************************************/
  if(find_types == FIND_TYPE_OBJ) {
    LIST *obj_list = newList();
    
    // get everything from our inventory
    if(IS_SET(find_scope, FIND_SCOPE_INV)) {
      LIST *inv_objs = find_all_objs(looker,charGetInventory(looker), at, NULL,
				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      OBJ_DATA *obj = NULL;
      while( (obj = listPop(inv_objs)) != NULL)
	if(!listIn(obj_list, obj))
	  listPut(obj_list, obj);
      deleteList(inv_objs);
    }

    // get everything from the room
    if(IS_SET(find_scope, FIND_SCOPE_ROOM)) {
      LIST *room_objs = find_all_objs(looker, 
				      roomGetContents(charGetRoom(looker)),
				      at, NULL,
				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      OBJ_DATA *obj = NULL;
      while( (obj = listPop(room_objs)) != NULL)
	if(!listIn(obj_list, obj))
	  listPut(obj_list, obj);
      deleteList(room_objs);
    }

    // get everything we are wearing
    if(IS_SET(find_scope, FIND_SCOPE_WORN)) {
      // we have to get a list of all eq as an intermediary step, and then
      // delete the list after we search through it again for everything
      // that we can see.
      LIST *equipment = bodyGetAllEq(charGetBody(looker));
      LIST *eq_objs = find_all_objs(looker, equipment, at, NULL,
				    (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      deleteList(equipment);
      OBJ_DATA *obj = NULL;
      while( (obj = listPop(eq_objs)) != NULL)
	if(!listIn(obj_list, obj))
	  listPut(obj_list, obj);
      deleteList(eq_objs);
    }

    // get everything in the world
    if(IS_SET(find_scope, FIND_SCOPE_WORLD)) {
      LIST *wld_objs = find_all_objs(looker,
				     object_list, at, NULL, 
				     (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      OBJ_DATA *obj = NULL;
      while( (obj = listPop(wld_objs)) != NULL)
	if(!listIn(obj_list, obj))
	  listPut(obj_list, obj);
      deleteList(wld_objs);
    }

    // if we didn't find anything, return NULL
    if(listSize(obj_list) < 1) {
      deleteList(obj_list);
      if(found_type)
	*found_type = FOUND_NONE;
      return NULL;
    }
    else
      return obj_list;
  }

  
  /************************************************************/
  /*                        FIND ALL CHARS                    */
  /************************************************************/
  else if(find_types == FIND_TYPE_CHAR) {
    LIST *char_list = newList();

    // find everyone in the room
    if(IS_SET(find_scope, FIND_SCOPE_ROOM)) {
      LIST *room_chars =find_all_chars(looker, 
				       roomGetCharacters(charGetRoom(looker)),
				       at, NULL,
				       (IS_SET(find_scope,FIND_SCOPE_VISIBLE)));
      CHAR_DATA *ch = NULL;
      while( (ch = listPop(room_chars)) != NULL)
	if(!listIn(char_list, ch))
	  listPut(char_list, ch);
      deleteList(room_chars);
    }

    // find everyone in the world
    if(IS_SET(find_scope, FIND_SCOPE_WORLD)) {
      LIST *wld_chars = find_all_chars(looker, 
				       mobile_list,
				       at, NULL,
				       (IS_SET(find_scope,FIND_SCOPE_VISIBLE)));
      CHAR_DATA *ch = NULL;
      while( (ch = listPop(wld_chars)) != NULL)
	if(!listIn(char_list, ch))
	  listPut(char_list, ch);
      deleteList(wld_chars);
    }

    // if we didn't find anything, return NULL
    if(listSize(char_list) < 1) {
      deleteList(char_list);
      if(found_type)
	*found_type = FOUND_NONE;
      return NULL;
    }
    else
      return char_list;
  }
  

  /************************************************************/
  /*                       FIND ALL EDESCS                    */
  /************************************************************/
  else {
    if(found_type)
      *found_type = FOUND_NONE;
    return NULL;
  }
}


void *find_one(CHAR_DATA *looker, 
	       int at_count, const char *at, 
	       bitvector_t find_types,
	       bitvector_t find_scope, int *found_type) {

  // find what we're looking AT
  int count = 0;

  /************************************************************/
  /*                   PERSONAL SEARCHES                      */
  /************************************************************/
  // see if its ourself
  if(IS_SET(find_types, FIND_TYPE_CHAR) &&
     at_count >= 1 && !strcasecmp(at, "self")) {
    at_count--;
    if(at_count == 0) {
      if(found_type)
	*found_type = FOUND_CHAR;
      return looker;
    }
  }

  // seach our inventory
  if(IS_SET(find_scope, FIND_SCOPE_INV) && 
     IS_SET(find_types, FIND_TYPE_OBJ)) {
    count = count_objs(looker, charGetInventory(looker), at, NULL, 
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      return find_obj(looker, charGetInventory(looker), at_count, at, NULL,
		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
      at_count -= count;
  }
  
  // search our equipment
  if(IS_SET(find_scope, FIND_SCOPE_WORN) &&
     IS_SET(find_types, FIND_TYPE_OBJ)) {
    LIST *equipment = bodyGetAllEq(charGetBody(looker));
    count = count_objs(looker, equipment, at, NULL, 
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      OBJ_DATA *obj = find_obj(looker, equipment, at_count, at, NULL, 
			       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
      deleteList(equipment);
      return obj;
    }
    else {
      deleteList(equipment);
      at_count -= count;
    }
  }


  /************************************************************/
  /*                      LOCAL SEARCHES                      */
  /************************************************************/
  // is it our current room?
  if(IS_SET(find_scope, FIND_SCOPE_ROOM | FIND_SCOPE_WORLD) &&
     IS_SET(find_types, FIND_TYPE_ROOM)  &&
     at_count == 1 && !strcasecmp(at, "room")) {
    if(found_type)
      *found_type = FOUND_ROOM;
    return charGetRoom(looker);
  }

  // search objects in the room
  if(IS_SET(find_scope, FIND_SCOPE_ROOM) && 
     IS_SET(find_types, FIND_TYPE_OBJ)) {
    count = count_objs(looker, roomGetContents(charGetRoom(looker)), at, NULL,
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      return find_obj(looker, roomGetContents(charGetRoom(looker)), at_count,
		      at, NULL, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
      at_count -= count;
  }

  // seach the characters in the room
  if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
     IS_SET(find_types, FIND_TYPE_CHAR)) {
    count = count_chars(looker, roomGetCharacters(charGetRoom(looker)), at,
		       NULL, (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_CHAR;
      return find_char(looker, roomGetCharacters(charGetRoom(looker)), 
		       at_count, at, NULL,
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
	at_count -= count;
  }

  // search for exits in the room
  if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
     IS_SET(find_types, FIND_TYPE_EXIT)) {
    EXIT_DATA *exit = roomGetExit(charGetRoom(looker), at);

    // no exit... are we using an abbreviation?
    if(exit == NULL && (dirGetAbbrevNum(at) != DIR_NONE))
      exit = roomGetExit(charGetRoom(looker), dirGetName(dirGetAbbrevNum(at)));

    // we found one
    if(exit && (!IS_SET(find_scope, FIND_SCOPE_VISIBLE) || 
		can_see_exit(looker, exit))) {
      at_count--;
      if(at_count == 0) {
	if(found_type)
	  *found_type = FOUND_EXIT;
	return exit;
      }
    }

    LIST       *ex_list = roomGetExitNames(charGetRoom(looker));
    LIST_ITERATOR *ex_i = newListIterator(ex_list);
    char           *dir = NULL;

    ITERATE_LIST(dir, ex_i) {
      exit = roomGetExit(charGetRoom(looker), dir);
      if(exitIsName(exit, at)) {
	if(!IS_SET(find_scope,FIND_SCOPE_VISIBLE) || can_see_exit(looker,exit)){
	  at_count--;
	  if(at_count == 0) {
	    if(found_type)
	      *found_type = FOUND_EXIT;
	    break;
	  }
	}
      }
    } deleteListIterator(ex_i);
    deleteListWith(ex_list, free);

    // we found one
    if(*found_type != FOUND_NONE)
      return exit;
  }

  // search extra descriptions in the room
  if(IS_SET(find_scope, FIND_SCOPE_ROOM) &&
     IS_SET(find_types, FIND_TYPE_EDESC)) {
    count = (roomGetEdesc(charGetRoom(looker), at) != NULL);
    if(count && at_count == 1) {
      if(found_type)
	*found_type = FOUND_EDESC;
      return edescSetGet(roomGetEdescs(charGetRoom(looker)), at);
    }
    else
	at_count--;
  }

  /************************************************************/
  /*                     GLOBAL SEARCHES                      */
  /************************************************************/
  // search rooms in the world
  if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
     IS_SET(find_types, FIND_TYPE_ROOM)   &&
     at_count == 1) {
    ROOM_DATA *room = worldGetRoom(gameworld, 
	                get_fullkey_relative(at, 
			  get_key_locale(roomGetClass(charGetRoom(looker)))));

    if(room != NULL) {
      if(found_type)
	*found_type = FOUND_ROOM;
      return room;
    }
  }

  // search objects in the world
  if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
     IS_SET(find_types, FIND_TYPE_OBJ)) {
    count = count_objs(looker, object_list, at, NULL, 
			 (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_OBJ;
      return find_obj(looker, object_list, at_count, at, NULL, 
		      (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
	at_count -= count;
  }

  // search characters in the world
  if(IS_SET(find_scope, FIND_SCOPE_WORLD) &&
     IS_SET(find_types, FIND_TYPE_CHAR)) {
    count = count_chars(looker, mobile_list, at, NULL, 
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    if(count >= at_count) {
      if(found_type)
	*found_type = FOUND_CHAR;
      return find_char(looker, mobile_list, at_count, at, NULL,
		       (IS_SET(find_scope, FIND_SCOPE_VISIBLE)));
    }
    else
	at_count -= count;
  }

  // we didn't find anything!
  if(found_type)
    *found_type = FOUND_NONE;
  return NULL;
}


void *generic_find(CHAR_DATA *looker, const char *arg,
		   bitvector_t find_types, 
		   bitvector_t find_scope, 
		   bool all_ok, int *found_type) {
  // make a working buffer...
  char working_arg[SMALL_BUFFER];
  strcpy(working_arg, arg);

  strip_word(working_arg, "the");

  char *at = at_arg(working_arg);
  char *in = in_arg(working_arg);
  char *on = on_arg(working_arg);

  strip_word(working_arg, "at");
  strip_word(working_arg, "in");
  strip_word(working_arg, "on");
  trim(working_arg);

  // if we don't have an "at", and there's a word we haven't pulled,
  // pull the first word before a space
  if(!at &&
     // figure out the difference between the
     // number of words that exist and the number
     // of words we've pulled. If it's > 0, we 
     // haven't pulled a word
     (count_letters(working_arg, ' ', strlen(working_arg)) + 1 - 
      (in != NULL) - (on != NULL)) == 1) {
    at = strtok(working_arg, " ");
    at = strdupsafe(at);
  }

  // make sure at, in, and on are never NULL
  // we always want to search by name, and never vnum
  if(!at) at = strdup("");
  if(!on) on = strdup("");
  if(!in) in = strdup("");

  // do the finding
  void *val = find_specific(looker, 
			    at, on, in,
			    find_types, find_scope, 
			    all_ok, found_type);

  if(at) free(at);
  if(in) free(in);
  if(on) free(on);

  return val;
}


//
// At is what we're looking for
// On is the thing we're looking for it on (e.g. "on sword" "on joe")
// In is the thing we're looking for it in (e.g. "in hole" "in bag")
//
void *find_specific(CHAR_DATA *looker,
		    const char *full_at, 
		    const char *full_on, 
		    const char *full_in,
		    bitvector_t find_types,
		    bitvector_t find_scope,
		    bool all_ok, int *found_type) {
  // for stuff like 2.sword, all.woman, etc...
  int at_count = 1;
  int on_count = 1;
  int in_count = 1;

  // the buffers for storing at, in, on separate from their counts
  char at[SMALL_BUFFER] = "";
  char in[SMALL_BUFFER] = "";
  char on[SMALL_BUFFER] = "";

  // separate the names from their numbers
  get_count(full_at, at, &at_count);
  get_count(full_in, in, &in_count);
  get_count(full_on, on, &on_count);

  if(found_type)
    *found_type = FOUND_NONE;


  // are we trying to find all of something?
  if(all_ok && at_count == COUNT_ALL && !*on && !*in)
    return find_all(looker, at, find_types, find_scope, found_type);

  else if(!*at || at_count == 0) {
    // we're trying to find what the contents of an item are?
    // e.g. "look in portal", "look in bag"
    if(*in && in_count >= 1 &&
       IS_SET(find_types, FIND_TYPE_IN_OBJ)) {
      void *tgt = NULL;
      int next_found_type = FOUND_NONE; // for finding in/on stuff
      char new_at[strlen(in) + 20]; // +20 for digits
      print_count(new_at, in, in_count);
      tgt = find_specific(looker, 
			  new_at, "", "",
			  find_types, find_scope,
			  all_ok, &next_found_type);

      // we couldn't find the thing we were trying to look inside
      if(!tgt)
	return NULL;
      // if we got a list of objects back, scrap 'em
      else if(next_found_type == FOUND_LIST) {
	deleteList(tgt);
	return NULL;
      }
      // we can only look inside of objects
      else if(next_found_type != FOUND_OBJ)
	return NULL;
      // return what we found
      else {
	if(found_type)
	  *found_type = FOUND_IN_OBJ;
	return tgt;
      }
    }
    // we're just not looking for anything
    else {
      if(found_type)
	*found_type = FOUND_NONE;
      return NULL;
    }
  }

  else {
    void *tgt  = NULL; // used for finding what we're looking in/on
    /****************************************************/
    /*                   START LOOK IN                  */
    /****************************************************/
    // check out what we're looking in
    if(*in && in_count > 0) {
      int next_found_type = FOUND_NONE; // for finding in/on stuff
      char new_at[strlen(in) + 20]; // +20 for digits
      print_count(new_at, in, in_count);
      tgt = find_specific(looker, 
			  new_at, "", "",
			  find_types, find_scope,
			  all_ok, &next_found_type);
      // we couldn't find the thing we were trying to look inside
      if(!tgt)
	return NULL;
      // we have to KILL someone before we can look inside of them ;)
      else if(next_found_type != FOUND_OBJ)
	return NULL;
      // apply another find, narrowing the scope to inside this object
      else
	return find_in_obj(looker, tgt, 
			   at_count, at, 
			   on_count, on,
			   find_types, find_scope, found_type);
    }


    /****************************************************/
    /*                   START LOOK ON                  */
    /****************************************************/
    // find out what we're looking on
    else if(*on && on_count > 0) {
      int next_found_type = FOUND_NONE;
      char new_at[strlen(on) + 20]; // +20 for digits
      print_count(new_at, on, on_count);
      tgt = find_specific(looker, 
			  on, "", "",
			  find_types, find_scope,
			  all_ok, &next_found_type);
      // couldn't find what we were trying to look on
      if(tgt == NULL)
	return NULL;
      else if(next_found_type == FOUND_CHAR)
	return find_on_char(looker, tgt, at_count, at, 
			    find_types, find_scope, found_type);
      else if(next_found_type == FOUND_OBJ) {
	return find_on_obj(looker, tgt, at_count, at, find_types, 
			   find_scope, found_type);
      }
      else
	return NULL;
    }


    /****************************************************/
    /*                   START LOOK AT                  */
    /****************************************************/
    return find_one(looker, at_count, at, find_types, find_scope, found_type);
  }
}