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/
//*****************************************************************************
//
// py_char.c
//
// A python extention to allow python scripts to treat MUD characters as an
// object within the script.
//
//*****************************************************************************

#include <Python.h>
#include <structmember.h>

#include "../mud.h"
#include "../world.h"
#include "../room.h"
#include "../character.h"
#include "../body.h"
#include "../object.h"
#include "../races.h"
#include "../handler.h"
#include "../utils.h"
#include "../action.h"
#include "../socket.h"
#include "../prototype.h"
#include "../save.h"

#include "pyplugs.h"
#include "scripts.h"
#include "pychar.h"
#include "pyroom.h"
#include "pyobj.h"
#include "pyexit.h"
#include "pyaccount.h"
#include "pyauxiliary.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../char_vars/char_vars.h"
#include "../items/items.h"
#include "../items/worn.h"



//*****************************************************************************
// local structures and defines
//*****************************************************************************
// a list of the get/setters on the Char class
LIST *pychar_getsetters = NULL;

// a list of the methods on the Char class
LIST *pychar_methods = NULL;

typedef struct {
  PyObject_HEAD
  int uid;
} PyChar;



//*****************************************************************************
// allocation, deallocation, initialization, and comparison
//*****************************************************************************
void PyChar_dealloc(PyChar *self) {
  self->ob_type->tp_free((PyObject*)self);
}

PyObject *PyChar_new(PyTypeObject *type, PyObject *args, PyObject *kwds){
    PyChar *self;
    self = (PyChar  *)type->tp_alloc(type, 0);
    self->uid = NOBODY;
    return (PyObject *)self;
}

int PyChar_init(PyChar *self, PyObject *args, PyObject *kwds) {
  static char *kwlist[] = {"uid", NULL};
  int uid = NOBODY;

  // get the universal id
  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &uid)) {
    PyErr_Format(PyExc_TypeError, 
                    "Characters may only be created by uid");
    return -1;
  }

  // make sure a character with the UID exists
  if(!propertyTableGet(mob_table, uid)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character with uid, %d, does not exist", uid);
    return -1;
  }

  self->uid = uid;
  return 0;
}


int PyChar_compare(PyChar *ch1, PyChar *ch2) {
  if(ch1->uid == ch2->uid)
    return 0;
  else if(ch1->uid < ch2->uid)
    return -1;
  else
    return 1;
}



//*****************************************************************************
// getters and setters for the Char class
//*****************************************************************************
PyObject *PyChar_getname(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetName(ch));
  else           return NULL;
}

PyObject *PyChar_getkeywords(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetKeywords(ch));
  else           return NULL;
}

PyObject *PyChar_getmname(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetMultiName(ch));
  else           return NULL;
}

PyObject *PyChar_getdesc(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetDesc(ch));
  else           return NULL;
}

PyObject *PyChar_getlookbuf(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", bufferString(charGetLookBuffer(ch)));
  else           return NULL;
}  

PyObject *PyChar_getrdesc(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetRdesc(ch));
  else           return NULL;
}

PyObject *PyChar_getmdesc(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetMultiRdesc(ch));
  else           return NULL;
}

PyObject *PyChar_getrace(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetRace(ch));
  else           return NULL;
}

PyObject *PyChar_getsex(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", sexGetName(charGetSex(ch)));
  else           return NULL;
}

PyObject *PyChar_getposition(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", posGetName(charGetPos(ch)));
  else           return NULL;
}

PyObject *PyChar_getroom(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL)
    return NULL;
  else if(charGetRoom(ch) != NULL)
    return Py_BuildValue("O", roomGetPyFormBorrowed(charGetRoom(ch)));
  else {
    Py_INCREF(Py_None);
    return Py_BuildValue("O", Py_None);
  }
}

PyObject *PyChar_getlastroom(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL)
    return NULL;
  else if(charGetLastRoom(ch) != NULL)
    return Py_BuildValue("O", roomGetPyFormBorrowed(charGetLastRoom(ch)));
  else {
    Py_INCREF(Py_None);
    return Py_BuildValue("O", Py_None);
  }
}

PyObject *PyChar_getisnpc(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("i", charIsNPC(ch));
  else           return NULL;
}

PyObject *PyChar_getispc(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("i", !charIsNPC(ch));
  else           return NULL;
}

PyObject *PyChar_gethisher(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", HISHER(ch));
  else           return NULL;
}

PyObject *PyChar_gethimher(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", HIMHER(ch));
  else           return NULL;
}

PyObject *PyChar_getheshe(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", HESHE(ch));
  else           return NULL;
}

PyObject *PyChar_geton(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) 
    return NULL;
  else if(charGetFurniture(ch) == NULL)
    return Py_BuildValue("O", Py_None);
  else 
    return Py_BuildValue("O", objGetPyFormBorrowed(charGetFurniture(ch)));
}

PyObject *PyChar_getuid(PyChar *self, void *closure) {
  return Py_BuildValue("i", self->uid);
}


PyObject *PyChar_getprototypes(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) return Py_BuildValue("s", charGetPrototypes(ch));
  else           return NULL;
}

PyObject *PyChar_getinv(PyChar *self, PyObject *args) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) 
    return NULL;

  LIST_ITERATOR *inv_i = newListIterator(charGetInventory(ch));
  PyObject *list = PyList_New(0);
  OBJ_DATA *obj;
  
  // for each obj in the inventory, add it to the Python list
  ITERATE_LIST(obj, inv_i)
    PyList_Append(list, objGetPyFormBorrowed(obj));
  deleteListIterator(inv_i);
  PyObject *retval = Py_BuildValue("O", list);
  Py_DECREF(list);
  return retval;
}

PyObject *PyChar_geteq(PyChar *self, PyObject *args) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL)
    return NULL;

  PyObject      *list = PyList_New(0);
  LIST      *equipped = bodyGetAllEq(charGetBody(ch));
  LIST_ITERATOR *eq_i = newListIterator(equipped);
  OBJ_DATA        *eq = NULL;
  ITERATE_LIST(eq, eq_i) {
    PyList_Append(list, objGetPyFormBorrowed(eq));
  } deleteListIterator(eq_i);
  PyObject *retval = Py_BuildValue("O", list);
  Py_DECREF(list);
  return retval;
}

PyObject *PyChar_getbodyparts(PyChar *self, PyObject *args) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) 
    return NULL;

  PyObject *list = PyList_New(0);
  int i, num_bodyparts;
  const char **bodyparts = bodyGetParts(charGetBody(ch), TRUE, &num_bodyparts);
  for(i = 0; i < num_bodyparts; i++) {
    PyObject *str = Py_BuildValue("s", bodyparts[i]);
    PyList_Append(list, str);
    Py_DECREF(str);
  }
  free(bodyparts);
  return list;
}

PyObject *PyChar_getusergroups(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) 
    return Py_BuildValue("s", bitvectorGetBits(charGetUserGroups(ch)));
  else           
    return NULL;
}

PyObject *PyChar_getsocket(PyChar *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL)
    return NULL;
  else {
    SOCKET_DATA *sock = charGetSocket(ch);
    if(sock == NULL)
      return Py_BuildValue("O", Py_None);
    return Py_BuildValue("O", socketGetPyFormBorrowed(sock));
  }
}



//
// Standard check to make sure the character exists when
// trying to set a value for it. If successful, assign the
// character to ch. Otherwise, return -1 (error)
#define PYCHAR_CHECK_CHAR_EXISTS(uid, ch)                                      \
  ch = propertyTableGet(mob_table, uid);                                       \
  if(ch == NULL) {                                                             \
    PyErr_Format(PyExc_TypeError,                                              \
		    "Tried to modify nonexistant character, %d", uid);         \
    return -1;                                                                 \
  }                                                                            


int PyChar_setname(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's name");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character names must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetName(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setkeywords(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's keywords");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character keywords must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetKeywords(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setmname(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's multi-name");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character multi-names must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetMultiName(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setdesc(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's description");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character descriptions must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetDesc(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setlookbuf(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's look buffer");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Look bufers must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  bufferClear(charGetLookBuffer(ch));
  bufferCat(charGetLookBuffer(ch), PyString_AsString(value));
  return 0;
}

int PyChar_setrdesc(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's rdesc");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character rdescs must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetRdesc(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setmdesc(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete character's multi-rdesc");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character multi-rdescs must be strings");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetMultiRdesc(ch, PyString_AsString(value));
  return 0;
}

int PyChar_setrace(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete a character's race");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Character races must be strings");
    return -1;
  }

  const char *race = PyString_AsString(value);
  if(!isRace(race))
    return -1;

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetRace(ch, race);
  charResetBody(ch);
  return 0;
}

int PyChar_seton(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete a character's furniture.");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);

  if (value == Py_None) {
    char_from_furniture(ch);
    return 0;
  }
  else if(PyObj_Check(value)) {
    OBJ_DATA *obj = propertyTableGet(obj_table, PyObj_AsUid(value));
    if(obj == NULL) {
      PyErr_Format(PyExc_TypeError, 
		   "Tried to %s's furniture to a nonexistant object.",
		   charGetName(ch));
      return -1;
    }
    else if(objIsType(obj, "furniture")) {
      if(charGetFurniture(ch))
	char_from_furniture(ch);
      char_to_furniture(ch, obj);
      return 0;
    }
  }

  PyErr_Format(PyExc_TypeError, 
	       "A Character's furniture may only be set to None or a ."
	       "furniture object.");
  return -1;
}

int PyChar_setsex(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete a character's sex");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character sexes must be strings");
    return -1;
  }

  int sex = sexGetNum(PyString_AsString(value));
  if(sex == SEX_NONE) {
    char buf[SMALL_BUFFER];
    sprintf(buf, "%s is an invalid sex type", PyString_AsString(value));
    PyErr_Format(PyExc_TypeError, buf);
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetSex(ch, sex);
  return 0;
}

int PyChar_setposition(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete a character's position");
    return -1;
  }
  
  if (!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character positions must be strings");
    return -1;
  }

  int pos = posGetNum(PyString_AsString(value));
  if(pos == POS_NONE) {
    char buf[SMALL_BUFFER];
    sprintf(buf, "%s is an invalid position type", PyString_AsString(value));
    PyErr_Format(PyExc_TypeError, buf);
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  charSetPos(ch, pos);
  // players can't be on furniture if they are standing or flying
  if(poscmp(charGetPos(ch), POS_STANDING) >= 0 && charGetFurniture(ch))
    char_from_furniture(ch);
  return 0;
}

int PyChar_setroom(PyChar *self, PyObject *value, void *closure) {
  if (value == NULL) {
    PyErr_Format(PyExc_TypeError, "Cannot delete a character's room");
    return -1;
  }

  ROOM_DATA *room = NULL;

  if(PyRoom_Check(value))
    room = PyRoom_AsRoom(value);
  else if(PyString_Check(value))
    room = worldGetRoom(gameworld, PyString_AsString(value));
  else {
    PyErr_Format(PyExc_TypeError, 
		 "Character's room must be a string value or a "
		 "room object.");
    return -1;
  }

  if(room == NULL) {
    PyErr_Format(PyExc_TypeError, 
		 "Attempting to move character to nonexistent room.");
    return -1;
  }

  CHAR_DATA *ch;
  PYCHAR_CHECK_CHAR_EXISTS(self->uid, ch);
  // only move if we're not already here
  if(charGetRoom(ch) != room) {
    char_to_room(ch, room);

    // if we were on furniture, make sure we dismount it
    if(charGetFurniture(ch))
      char_from_furniture(ch);
  }

  return 0;
}



//*****************************************************************************
// methods for the Char class
//*****************************************************************************

//
// pages a length of text to the character
PyObject *PyChar_page(PyChar *self, PyObject *value) {
  char *mssg = NULL;
  if (!PyArg_ParseTuple(value, "s", &mssg)) {
    PyErr_Format(PyExc_TypeError, "Characters may only be paged strings");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) {
    if(charGetSocket(ch))
      page_string(charGetSocket(ch), mssg);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_TypeError, 
                    "Tried to page message to nonexistant character, %d.", 
		    self->uid);
    return NULL;
  }
}

//
// sends text to the character
PyObject *PyChar_send_raw(PyChar *self, PyObject *value) {
  char *mssg = NULL;
  if (!PyArg_ParseTuple(value, "s", &mssg)) {
    PyErr_Format(PyExc_TypeError, 
                    "Characters may only be sent strings");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch) {
    send_to_char(ch, "%s", mssg);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_TypeError, 
                    "Tried to send message to nonexistant character, %d.", 
		    self->uid);
    return NULL;
  }
}

//
// sends a newline-tagged message to the character
PyObject *PyChar_send(PyChar *self, PyObject *value) {
  PyObject *retval = PyChar_send_raw(self, value);
  if(retval == NULL)
    return NULL;
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  send_to_char(ch, "\r\n");
  Py_DECREF(retval);
  return Py_BuildValue("i", 1);
}

//
// Send a newline-tagged message to everyone around the character
PyObject *PyChar_sendaround(PyChar *self, PyObject *value) {
  char *mssg = NULL;
  if (!PyArg_ParseTuple(value, "s", &mssg)) {
    PyErr_Format(PyExc_TypeError, 
                    "Characters may only be sent strings");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch) {
    send_around_char(ch, FALSE, "%s\r\n", mssg);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_TypeError, 
                    "Tried to send message to nonexistant character, %d.", 
		    self->uid);
    return NULL;
  }
}


//
// make the character perform an action
PyObject *PyChar_act(PyChar *self, PyObject *value) {
  char     *act = NULL;
  bool alias_ok = FALSE; 
  if (!PyArg_ParseTuple(value, "s|b", &act, &alias_ok)) {
    PyErr_Format(PyExc_TypeError, "Characters actions must be strings.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch) {
    // do not send the actual act - if we edit it, things go awry
    char *working_act = strdupsafe(act);
    do_cmd(ch, working_act, alias_ok);
    free(working_act);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_TypeError, 
                    "Nonexistant character, %d, tried to perform an action.", 
		    self->uid);
    return NULL;
  }
}


//
// returns whether or not the character can see something
PyObject *PyChar_cansee(PyChar *self, PyObject *arg) {
  PyObject *py_tgt = NULL;

  if(!PyArg_ParseTuple(arg, "O", &py_tgt)) {
    PyErr_Format(PyExc_TypeError, "Must supply obj, mob, or exit for cansee");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);  
  if(ch == NULL) {
    PyErr_Format(PyExc_TypeError, "Nonexistent character, %d, tried cansee",
		 self->uid);
    return NULL;
  }
  else {
    OBJ_DATA    *obj = NULL;
    CHAR_DATA  *pers = NULL;
    EXIT_DATA    *ex = NULL;

    if(PyChar_Check(py_tgt))
      pers = PyChar_AsChar(py_tgt);
    else if(PyObj_Check(py_tgt))
      obj  = PyObj_AsObj(py_tgt);
    else if(PyExit_Check(py_tgt))
      ex   = PyExit_AsExit(py_tgt);
    else {
      PyErr_Format(PyExc_TypeError, "Must supply obj, mob, or exit to cansee");
      return NULL;
    }

    if(obj != NULL)
      return Py_BuildValue("b", can_see_obj(ch, obj));
    else if(pers != NULL)
      return Py_BuildValue("b", can_see_char(ch, pers));
    else if(ex != NULL)
      return Py_BuildValue("b", can_see_exit(ch, ex));
    else {
      PyErr_Format(PyExc_StandardError, "Target of cansee did not exist!");
      return NULL;
    }
  }
}


//
// Returns TRUE if the character has the given variable set
PyObject *PyChar_hasvar(PyChar *self, PyObject *arg) {
  char *var = NULL;
  if (!PyArg_ParseTuple(arg, "s", &var)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character variables must have string names.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL)
    return Py_BuildValue("b", charHasVar(ch, var));

  PyErr_Format(PyExc_TypeError, 
	       "Tried to get a variable value for nonexistant character, %d",
	       self->uid);
  return NULL;
}


//
// Delete the variable set on the character with the specified name
PyObject *PyChar_deletevar(PyChar *self, PyObject *arg) {
  char *var = NULL;
  if (!PyArg_ParseTuple(arg, "s", &var)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character variables must have string names.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) {
    charDeleteVar(ch, var);
    return Py_BuildValue("i", 1);
  }

  PyErr_Format(PyExc_TypeError, 
	       "Tried to get a variable value for nonexistant character, %d",
	       self->uid);
  return NULL;
}


//
// Get the value of a variable stored on the character
PyObject *PyChar_getvar(PyChar *self, PyObject *arg) {
  char *var = NULL;
  if (!PyArg_ParseTuple(arg, "s", &var)) {
    PyErr_Format(PyExc_TypeError, 
                    "Character variables must have string names.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) {
    int vartype = charGetVarType(ch, var);
    if(vartype == CHAR_VAR_INT)
      return Py_BuildValue("i", charGetInt(ch, var));
    else if(vartype == CHAR_VAR_LONG)
      return Py_BuildValue("i", charGetLong(ch, var));
    else if(vartype == CHAR_VAR_DOUBLE)
      return Py_BuildValue("d", charGetDouble(ch, var));
    else
      return Py_BuildValue("s", charGetString(ch, var));
  }
  else {
    PyErr_Format(PyExc_TypeError, 
		 "Tried to get a variable value for nonexistant character, %d",
		 self->uid);
    return NULL;
  }
}


//
// Set the value of a variable assocciated with the character
PyObject *PyChar_setvar(PyChar *self, PyObject *args) {  
  char     *var = NULL;
  PyObject *val = NULL;

  if (!PyArg_ParseTuple(args, "sO", &var, &val)) {
    PyErr_Format(PyExc_TypeError, 
		 "Character setvar must be supplied with a var name and integer value.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) {
    if(PyInt_Check(val))
      charSetInt(ch, var, (int)PyInt_AsLong(val));
    else if(PyFloat_Check(val))
      charSetDouble(ch, var, PyFloat_AsDouble(val));
    else if(PyString_Check(val))
      charSetString(ch, var, PyString_AsString(val));
    else {
      PyErr_Format(PyExc_TypeError,
		   "Tried to store a char_var of invalid type on char %d.",
		   self->uid);
      return NULL;
    }
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_TypeError, 
		 "Tried to set a variable value for nonexistant character, %d",
		 self->uid);
    return NULL;
  }
}


PyObject *PyChar_getbodypct(PyChar *self, PyObject *args) {
  char   *parts = NULL;
  CHAR_DATA *ch = NULL;

  if (!PyArg_ParseTuple(args, "s", &parts)) {
    PyErr_Format(PyExc_TypeError,"A comma-separated list of body parts needed");
    return NULL;
  }

  ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to query body info for nonexistant character!");
    return NULL;
  }

  return Py_BuildValue("d", bodyPartRatio(charGetBody(ch), parts));
}

//
// equips a character with an item
PyObject *PyChar_equip(PyChar *self, PyObject *args) {  
  OBJ_DATA  *obj = NULL;
  CHAR_DATA  *ch = NULL;
  PyObject *pobj = NULL;
  char      *pos = NULL;

  // incase the equip fails, keep item in the original place.. here's the vars
  CHAR_DATA *old_carrier = NULL;
  CHAR_DATA  *old_wearer = NULL;
  const char    *old_pos = NULL;
  ROOM_DATA    *old_room = NULL;
  OBJ_DATA     *old_cont = NULL;

  if (!PyArg_ParseTuple(args, "O|z", &pobj, &pos)) {
    PyErr_Format(PyExc_TypeError, 
		 "Character equip must be supplied with an item to equip!");
    return NULL;
  }

  if(!PyObj_Check(pobj)) {
    PyErr_Format(PyExc_TypeError,
		 "Only objects may be equipped to characters!");
    return NULL;
  }

  ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to equip nonexistant character!");
    return NULL;
  }

  obj = PyObj_AsObj(pobj);
  if(obj == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to equip character with existant object!");
    return NULL;
  }

  // remove the object from whatever it's in/on currently
  if((old_room = objGetRoom(obj)) != NULL)
    obj_from_room(obj);
  if((old_cont = objGetContainer(obj)) != NULL)
    obj_from_obj(obj);
  if((old_carrier = objGetCarrier(obj)) != NULL)
    obj_from_char(obj);
  if((old_wearer = objGetWearer(obj)) != NULL) {
    old_pos = bodyEquippedWhere(charGetBody(old_wearer), obj);
    try_unequip(old_wearer, obj);
  }

  // try equipping the object. If we fail, put it back wherever it came from
  if(!objIsType(obj, "worn") || !try_equip(ch, obj, pos,wornGetPositions(obj))){
    if(old_room != NULL)
      obj_to_room(obj, old_room);
    else if(old_cont != NULL)
      obj_to_obj(obj, old_cont);
    else if(old_carrier != NULL)
      obj_to_char(obj, old_carrier);
    else if(old_wearer != NULL)
      try_equip(ch, obj, old_pos, NULL);
    return Py_BuildValue("i", 0);
    //    PyErr_Format(PyExc_StandardError,
    //		 "Character is already equipped in all possible positions!");
    //    return NULL;
  }
  // success
  else 
    return Py_BuildValue("i", 1);
}


PyObject *PyChar_getequip(PyChar *self, PyObject *args) {  
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  OBJ_DATA *obj = NULL;
  char     *pos = NULL;
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "Nonexistant character");
    return NULL;
  }

  if(!PyArg_ParseTuple(args, "s", &pos)) {
    PyErr_Format(PyExc_TypeError, "A position name must be supplied.");
    return NULL;
  }
  
  obj = bodyGetEquipment(charGetBody(ch), pos);
  if(obj == NULL)
    return Py_BuildValue("O", Py_None);
  else
    return Py_BuildValue("O", objGetPyFormBorrowed(obj));
}


PyObject *PyChar_attach(PyChar *self, PyObject *args) {  
  char *key = NULL;

  // make sure we're getting passed the right type of data
  if (!PyArg_ParseTuple(args, "s", &key)) {
    PyErr_Format(PyExc_TypeError, 
		 "To attach a trigger, the trigger key must be suppplied.");
    return NULL;
  }

  // pull out the character and do the attaching
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to attach trigger to nonexistant char, %d.", self->uid);
    return NULL;
  }

  TRIGGER_DATA *trig = 
    worldGetType(gameworld, "trigger", 
		 get_fullkey_relative(key, get_script_locale()));
  if(trig != NULL) {
    triggerListAdd(charGetTriggers(ch), triggerGetKey(trig));
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to attach nonexistant trigger, %s, to character %s.",
		 key, charGetClass(ch));
    return NULL;
  }
}


PyObject *PyChar_detach(PyChar *self, PyObject *args) {  
  char *key = NULL;

  // make sure we're getting passed the right type of data
  if (!PyArg_ParseTuple(args, "s", &key)) {
    PyErr_Format(PyExc_TypeError, 
		 "To detach a trigger, the key must be supplied.");
    return NULL;
  }

  // pull out the character and do the attaching
  CHAR_DATA    *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL) {
    const char *fkey = get_fullkey_relative(key, get_script_locale());
    triggerListRemove(charGetTriggers(ch), fkey);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_StandardError, 
		"Tried to detach trigger from nonexistant char, %d.",self->uid);
    return NULL;
  }
}


//
// handles the completion of an action queued up by Python
void PyAction_on_complete(CHAR_DATA *ch, PyObject *tuple, bitvector_t where,
			  const char *arg) {
  PyObject *pychar = NULL; // the python representation of our ch
  PyObject  *cfunc = NULL; // the function called on completion
  PyObject  *ifunc = NULL; // the function called on interruption
  PyObject   *data = NULL; // the data we need to send back

  // only run the action of our arguments parse properly
  if(PyArg_ParseTuple(tuple, "OOOO", &pychar, &data, &cfunc, &ifunc)) {
    if(cfunc != Py_None) {
      PyObject *ret = PyObject_CallFunction(cfunc, "OOs", pychar, data, arg);
      Py_XDECREF(ret);
    }
  }
  
  Py_DECREF(tuple);
}


//
// handles the interruption of an action queued up by Python
void PyAction_on_interrupt(CHAR_DATA *ch, PyObject *tuple, bitvector_t where,
			   const char *arg) {
  PyObject *pychar = NULL; // the python representation of our ch
  PyObject  *cfunc = NULL; // the function called on completion
  PyObject  *ifunc = NULL; // the function called on interruption
  PyObject   *data = NULL; // the data we need to send back

  // only run the action of our arguments parse properly
  if(PyArg_ParseTuple(tuple, "OOOO", &pychar, &data, &cfunc, &ifunc)) {
    if(ifunc != Py_None) {
      PyObject *ret = PyObject_CallFunction(ifunc, "OOs", pychar, data, arg);
      Py_XDECREF(ret);
    }
  }
  
  Py_DECREF(tuple);
}


//
// start a new action (and interrupt old ones)
PyObject *PyChar_start_action(PyChar *self, PyObject *args) {  
  CHAR_DATA          *ch = NULL;    // our normal character representation
  PyObject  *on_complete = Py_None; // func called when action is completed
  PyObject *on_interrupt = Py_None; // func called when action is interrupted
  PyObject         *data = Py_None; // the function's data value
  double           delay = 0;       // the delay of the action (seconds)
  char              *arg = NULL;    // the action's string argument

  // parse all of our values
  if(!PyArg_ParseTuple(args, "dO|OOs", &delay,  &on_complete, &on_interrupt,
		      &data, &arg)) {
    PyErr_Format(PyExc_TypeError,
		 "startAction supplied with invalid arguments!");
    return NULL;
  }

  // make sure we exist
  if((ch = PyChar_AsChar((PyObject *)self)) == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to start action for nonexistant character!");
    return NULL;
  }

  // now, queue up the action
  start_action(ch, (int)(delay SECONDS), 1, 
	       PyAction_on_complete, PyAction_on_interrupt, 
	       Py_BuildValue("OOOO", self, data, on_complete, on_interrupt),
	       arg);

  // success!
  return Py_BuildValue("i", 1);
}


//
// check to see if a character currently has an action in progress
PyObject *PyChar_is_acting(PyChar *self, PyObject *args) {  
  // make sure we exist
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);

  // make sure the character exists
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to query action status for a nonexistant character.");
    return NULL;
  }

  // return our value
  return Py_BuildValue("i", is_acting(ch, 1));
}


//
// interrupt any actions the character is currently performing
PyObject *PyChar_interrupt_action(PyChar *self, PyObject *args) {  
  // make sure we exist
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);

  // make sure the character exists
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to interrupt actions for a nonexistant character.");
    return NULL;
  }

  interrupt_action(ch, 1);
  return Py_BuildValue("i", 1);
}


//
// returns the specified piece of auxiliary data from the character
// if it is a piece of python auxiliary data.
PyObject *PyChar_get_auxiliary(PyChar *self, PyObject *args) {
  char *keyword = NULL;
  if(!PyArg_ParseTuple(args, "s", &keyword)) {
    PyErr_Format(PyExc_TypeError,
		 "getAuxiliary() must be supplied with the name that the "
		 "auxiliary data was installed under!");
    return NULL;
  }

  // make sure we exist
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError,
		 "Tried to get auxiliary data for a nonexistant character.");
    return NULL;
  }

  // make sure the auxiliary data exists
  if(!pyAuxiliaryDataExists(keyword)) {
    PyErr_Format(PyExc_StandardError,
		 "No auxiliary data named '%s' exists!", keyword);
    return NULL;
  }

  PyObject *data = charGetAuxiliaryData(ch, keyword);
  if(data == NULL)
    data = Py_None;
  PyObject *retval = Py_BuildValue("O", data);
  //  Py_DECREF(data);
  return retval;
}


//
// returns whether or not the character is an instance of the prototype
PyObject *PyChar_isinstance(PyChar *self, PyObject *args) {  
  char *type = NULL;

  // make sure we're getting passed the right type of data
  if (!PyArg_ParseTuple(args, "s", &type)) {
    PyErr_Format(PyExc_TypeError, "isinstance only accepts strings.");
    return NULL;
  }

  // pull out the object and check the type
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL)
    return Py_BuildValue("i", 
        charIsInstance(ch, get_fullkey_relative(type, get_script_locale())));
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to check instances of nonexistent char, %d.", self->uid);
    return NULL;
  }
}

//
// returns whether or not the character belongs to one of the specified groups
PyObject *PyChar_is_in_groups(PyChar *self, PyObject *args) {  
  char *groups = NULL;

  // make sure we're getting passed the right type of data
  if (!PyArg_ParseTuple(args, "s", &groups)) {
    PyErr_Format(PyExc_TypeError, "is_in_groups only accepts strings.");
    return NULL;
  }

  // pull out the object and check the type
  CHAR_DATA *ch = PyChar_AsChar((PyObject *)self);
  if(ch != NULL)
    return Py_BuildValue("i", bitIsSet(charGetUserGroups(ch), groups));
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to check user groups of nonexistent char, %d.", self->uid);
    return NULL;
  }
}



//*****************************************************************************
// comparators, getattr, setattr, and all that other class stuff
//*****************************************************************************
PyTypeObject PyChar_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "char.Char",               /*tp_name*/
    sizeof(PyChar),            /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)PyChar_dealloc,/*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    (cmpfunc)PyChar_compare,   /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
    "Char/Mob objects",        /* tp_doc */
    0,		               /* tp_traverse */
    0,		               /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    0,                         /* tp_methods */
    0,                         /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)PyChar_init,     /* tp_init */
    0,                         /* tp_alloc */
    PyChar_new,                /* tp_new */
};



//*****************************************************************************
// methods in the char module
//*****************************************************************************
PyObject *PyChar_load_mob(PyObject *self, PyObject *args) {
  char      *mob_key = NULL;
  PyObject       *to = NULL;
  ROOM_DATA    *room = NULL;
  OBJ_DATA       *on = NULL;
  char      *posname = NULL;
  
  if (!PyArg_ParseTuple(args, "sO|s", &mob_key, &to, &posname)) {
    PyErr_Format(PyExc_TypeError, 
		 "Load char failed - it needs prototype and destination.");
    return NULL;
  }

  // see what we're trying to load to
  if(PyString_Check(to))
    room = worldGetRoom(gameworld, PyString_AsString(to));
  else if(PyRoom_Check(to))
    room = PyRoom_AsRoom(to);
  else if(PyObj_Check(to))
    on = propertyTableGet(obj_table, PyObj_AsUid(to));
  else {
    PyErr_Format(PyExc_TypeError, 
                    "Load char failed: invalid load-to type.");
    return NULL;
  }

  // see if we're loading onto something
  if(on != NULL)
    room = objGetRoom(on);

  if(room == NULL) {
    PyErr_Format(PyExc_TypeError, "Load char failed: room does not exist, or "
		 "furniture is not. in a room.");
    return NULL;
  }

  // check the mob
  PROTO_DATA *mob_proto = 
    worldGetType(gameworld, "mproto", 
		 get_fullkey_relative(mob_key, get_script_locale()));
  if(mob_proto == NULL) {
    PyErr_Format(PyExc_TypeError, 
		 "Load char failed: no mproto for %s exists", 
		 get_fullkey_relative(mob_key, get_script_locale()));
    return NULL;
  }

  // copy the mob, and put it into the game
  CHAR_DATA *mob = protoMobRun(mob_proto);
  if(mob == NULL) {
    PyErr_Format(PyExc_TypeError,
		 "Load char failed: proto script terminated with an error.");
    return NULL;
  }

  char_to_room(mob, room);

  // now check if we need to put the char onto some furniture
  // default position is POS_SITTING
  if(on) {
    int pos = POS_SITTING;
    char_to_furniture(mob, on);
    if(posname)
      pos = posGetNum(posname);

    // if the position is none, or greater 
    // than sitting, default to sitting.
    if(pos == POS_NONE || poscmp(pos, POS_SITTING) > 0)
      pos = POS_SITTING;
    charSetPos(mob, pos);
  }
  else if(posname) {
    int pos = posGetNum(posname);
    // if it was an invalid name, set it to standing
    if(pos == POS_NONE)
      pos = POS_STANDING;
    charSetPos(mob, pos);
  }

  // create a python object for the new char, and return it
  return Py_BuildValue("O", charGetPyFormBorrowed(mob));
}

PyObject *PyChar_find_char_key(PyObject *self, PyObject *args) {
  CHAR_DATA     *ch = NULL;
  PyObject    *pych = NULL;
  LIST       *where = mobile_list;
  PyObject *pywhere = NULL;
  char         *key = NULL;
  bool     must_see = TRUE;
  bool     multiple = FALSE;
  ROOM_DATA   *room = NULL;

  // figure out our arguments
  if(!PyArg_ParseTuple(args, "Os|Obb",&pych,&key,&pywhere,&must_see,&multiple)){
    PyErr_Format(PyExc_TypeError, "Invalid arguments supplied to find_char");
    return NULL;
  }

  // make sure ch exists, if we supplied one
  if(pych == Py_None)
    ch = NULL;
  else if(PyChar_Check(pych)) {
    ch = PyChar_AsChar(pych);
    if(ch == NULL) {
      PyErr_Format(PyExc_StandardError, "character does not exist");
      return NULL;
    }
  }
  else {
    PyErr_Format(PyExc_TypeError, "first arg must be a Char, or None");
    return NULL;
  }

  // figoure out our room if we supply one
  if(pywhere != NULL) {
    if(PyRoom_Check(pywhere))
      room = PyRoom_AsRoom(pywhere);
    else if(PyString_Check(pywhere))
      room = worldGetRoom(gameworld, 
			  get_fullkey_relative(PyString_AsString(pywhere),
					       get_script_locale()));
    else if(pywhere == Py_None)
      room = NULL;
    else {
      PyErr_Format(PyExc_TypeError, "search scope must be a room or room key");
      return NULL;
    }
  }

  // if we've got a room, look in it
  if(room != NULL)
    where = roomGetCharacters(room);

  // do the searching for a single thing
  if(multiple == FALSE) {
    CHAR_DATA *found = find_char(ch, where, 1, NULL, 
				 get_fullkey_relative(key, get_script_locale()),
				 must_see);
    return Py_BuildValue("O", (found ? charGetPyFormBorrowed(found) : Py_None));
  }
  // search for multiple occurences
  else {
    LIST *found = find_all_chars(ch, where, NULL,
				 get_fullkey_relative(key, get_script_locale()),
				 must_see);
    PyObject         *list = PyList_New(0);
    LIST_ITERATOR *found_i = newListIterator(found);
    CHAR_DATA   *one_found = NULL;
    ITERATE_LIST(one_found, found_i) {
      PyList_Append(list, charGetPyFormBorrowed(one_found));
    } deleteListIterator(found_i);
    deleteList(found);
    PyObject *retval = Py_BuildValue("O", list);
    Py_DECREF(list);
    return retval;
  }
}

PyObject *PyChar_count_mobs(PyObject *self, PyObject *args) {
  LIST            *list = NULL;
  char             *tgt = NULL;
  PyObject          *in = NULL;
  ROOM_DATA       *room = NULL;
  OBJ_DATA   *furniture = NULL;
  const char *prototype = NULL;

  if (!PyArg_ParseTuple(args, "s|O", &tgt, &in)) {
    PyErr_Format(PyExc_TypeError, 
                    "count_mobs failed. No arguments supplied.");
    return NULL;
  }

  // figure out the full key of our prototype
  prototype = get_fullkey_relative(tgt, get_script_locale());

  // if we didn't supply something to look in, assume it means the world
  if(in == NULL)
    return Py_BuildValue("i", count_chars(NULL, mobile_list, NULL, prototype, 
					  FALSE));

  // see what we're looking in
  if(PyString_Check(in))
    room = worldGetRoom(gameworld, PyString_AsString(in));
  else if(PyRoom_Check(in))
    room = PyRoom_AsRoom(in);
  else if(PyObj_Check(in))
    furniture = propertyTableGet(obj_table, PyObj_AsUid(in));

  // now find the list we're dealing with
  if(room)      list = roomGetCharacters(room);
  if(furniture) list = objGetUsers(furniture);

  if(list == NULL) {
    PyErr_Format(PyExc_TypeError, 
		 "count_mobs failed. invalid argument supplied.");
    return NULL;
  }
  
  return Py_BuildValue("i", count_chars(NULL, list, NULL, prototype, FALSE));
}

PyObject *PyChar_all_chars(PyObject *self) {
  PyObject      *list = PyList_New(0);
  LIST_ITERATOR *ch_i = newListIterator(mobile_list);
  CHAR_DATA       *ch = NULL;
  ITERATE_LIST(ch, ch_i)
    PyList_Append(list, charGetPyFormBorrowed(ch));
  deleteListIterator(ch_i);
  PyObject *retval = Py_BuildValue("O", list);
  Py_DECREF(list);
  return retval;
}

PyMethodDef char_module_methods[] = {
  { "char_list", (PyCFunction)PyChar_all_chars, METH_NOARGS,
    "Return a python list containing an entry for every character in game." },
  { "load_mob", PyChar_load_mob, METH_VARARGS,
    "load a mobile with the specified prototype to a room." },
  { "count_mobs", PyChar_count_mobs, METH_VARARGS,
    "count how many occurances of a mobile there are in the specified scope. "
    "prototype or name can be used." },
  { "find_char_key", PyChar_find_char_key, METH_VARARGS,
    "finds a character (or group of chars) by their prototype. Finding by "
    "keywords is done with generic_find()" },
  {NULL, NULL, 0, NULL}  /* Sentinel */
};



//*****************************************************************************
// implementation of pychar.h
//*****************************************************************************
void PyChar_addGetSetter(const char *name, void *g, void *s, const char *doc) {
  // make sure our list of get/setters is created
  if(pychar_getsetters == NULL) pychar_getsetters = newList();

  // make the GetSetter def
  PyGetSetDef *def = calloc(1, sizeof(PyGetSetDef));
  def->name        = strdup(name);
  def->get         = (getter)g;
  def->set         = (setter)s;
  def->doc         = (doc ? strdup(doc) : NULL);
  def->closure     = NULL;
  listPut(pychar_getsetters, def);
}

void PyChar_addMethod(const char *name, void *f, int flags, const char *doc) {
  // make sure our list of methods is created
  if(pychar_methods == NULL) pychar_methods = newList();

  // make the Method def
  PyMethodDef *def = calloc(1, sizeof(PyMethodDef));
  def->ml_name     = strdup(name);
  def->ml_meth     = (PyCFunction)f;
  def->ml_flags    = flags;
  def->ml_doc      = (doc ? strdup(doc) : NULL);
  listPut(pychar_methods, def);
}



PyMODINIT_FUNC init_PyChar(void) {
  PyObject* m;

  // add in our setters and getters for the char class
  PyChar_addGetSetter("inv", PyChar_getinv, NULL,
		      "returns a list of objects in the char's inventory");
  PyChar_addGetSetter("objs", PyChar_getinv, NULL,
		      "returns a list of objects in the char's inventory");
  PyChar_addGetSetter("eq",   PyChar_geteq,  NULL,
		      "returns a list of the character's equipment");
  PyChar_addGetSetter("bodyparts", PyChar_getbodyparts, NULL,
		      "Returns a list of the character's bodyparts");
  PyChar_addGetSetter("name", PyChar_getname, PyChar_setname,
		      "handle the character's name");
  PyChar_addGetSetter("mname", PyChar_getmname, PyChar_setmname,
		      "handle the character's multi-name");
  PyChar_addGetSetter("desc", PyChar_getdesc, PyChar_setdesc,
		      "handle the character's description");
  PyChar_addGetSetter("look_buf", PyChar_getlookbuf, PyChar_setlookbuf,
		      "handle the character's look buffer");
  PyChar_addGetSetter("rdesc", PyChar_getrdesc, PyChar_setrdesc,
		      "handle the character's room description");
  PyChar_addGetSetter("mdesc", PyChar_getmdesc, PyChar_setmdesc,
		      "handle the character's multi room description");
  PyChar_addGetSetter("keywords", PyChar_getkeywords, PyChar_setkeywords,
		      "comma-separated list of the character's keywords.");
  PyChar_addGetSetter("sex", PyChar_getsex, PyChar_setsex,
		      "handle the character's gender");
  PyChar_addGetSetter("gender", PyChar_getsex, PyChar_setsex,
		      "handle the character's gender");
  PyChar_addGetSetter("race", PyChar_getrace, PyChar_setrace,
		      "handle the character's race");
  PyChar_addGetSetter("pos", PyChar_getposition, PyChar_setposition,
		      "handle the character's position");
  PyChar_addGetSetter("position", PyChar_getposition, PyChar_setposition,
		      "handle the character's position");
  PyChar_addGetSetter("room", PyChar_getroom, PyChar_setroom,
		      "handle the character's room");
  PyChar_addGetSetter("last_room", PyChar_getlastroom, NULL,
		      "the last room the character was in");
  PyChar_addGetSetter("on", PyChar_geton, PyChar_seton,
   "The furniture the character is sitting on/at. If the character is not "
   "on furniture, None is returned. To remove a character from furniture, "
  "then use None");
  PyChar_addGetSetter("uid", PyChar_getuid, NULL,
		      "the character's unique identification number");
  PyChar_addGetSetter("prototypes", PyChar_getprototypes, NULL,
		      "The prototypes for a mobile");
  PyChar_addGetSetter("is_npc", PyChar_getisnpc, NULL,
		      "Returns 1 if the char is an NPC, and 0 otherwise.");
  PyChar_addGetSetter("is_pc", PyChar_getispc, NULL,
		      "Returns 1 if the char is a PC, and 0 otherwise.");
  PyChar_addGetSetter("hisher", PyChar_gethisher, NULL,
		      "Returns 'his' if the char is male, 'her' if female, and "
		      "'its' for neuters");
  PyChar_addGetSetter("himher", PyChar_gethimher, NULL,
		      "Returns 'him' if the char is male, 'her' if female, and "
		      "'it' for neuters");
  PyChar_addGetSetter("heshe", PyChar_getheshe, NULL,
		      "Returns 'he' if the char is male, 'she' if female, and "
		      "'it' for neuters");
  PyChar_addGetSetter("user_groups", PyChar_getusergroups, NULL,
		      "Returns the character's user groups");
  PyChar_addGetSetter("socket", PyChar_getsocket, NULL,
		      "Returns the character's socket if it exists.");
  PyChar_addGetSetter("sock",   PyChar_getsocket, NULL,
		      "Returns the character's socket if it exists.");

  // add in all of our methods for the Char class
  PyChar_addMethod("attach", PyChar_attach, METH_VARARGS,
		   "attach a new script to the character.");
  PyChar_addMethod("detach", PyChar_detach, METH_VARARGS,
		   "detach an old script from the character.");
  PyChar_addMethod("send", PyChar_send, METH_VARARGS,
		   "send a message to the character with appended newline.");
  PyChar_addMethod("send_raw", PyChar_send_raw, METH_VARARGS,
		   "send a message to the character.");
  PyChar_addMethod("sendaround", PyChar_sendaround, METH_VARARGS,
		   "send a message to everyone around the character.");
  PyChar_addMethod("act", PyChar_act, METH_VARARGS,
		   "make the character perform an action.");
  PyChar_addMethod("getvar", PyChar_getvar, METH_VARARGS,
		   "get the value of a special variable the character has.");
  PyChar_addMethod("setvar", PyChar_setvar, METH_VARARGS,
		   "set the value of a special variable the character has.");
  PyChar_addMethod("hasvar", PyChar_hasvar, METH_VARARGS,
		   "return whether or not the character has a given variable.");
  PyChar_addMethod("deletevar", PyChar_deletevar, METH_VARARGS,
		   "delete a variable from the character's variable table.");
  PyChar_addMethod("delvar", PyChar_deletevar, METH_VARARGS,
		   "delete a variable from the character's variable table.");
  PyChar_addMethod("equip", PyChar_equip, METH_VARARGS,
		   "equips a character with the given item. Removes the item "
		   "from whatever it is currently in/on.");
  PyChar_addMethod("get_equip", PyChar_getequip, METH_VARARGS,
		   "Returns the person's equipment in the specified slot.");
  PyChar_addMethod("get_bodypct", PyChar_getbodypct, METH_VARARGS,
		   "Returns the percent mass of the character's body taken up "
		   "by the specified parts.");
  PyChar_addMethod("isActing", PyChar_is_acting, METH_VARARGS,
		   "Returns True if the character is currently taking an "
		   "action, and False otherwise.");
  PyChar_addMethod("startAction", PyChar_start_action, METH_VARARGS,
		   "Begins the character starting a new action");
  PyChar_addMethod("interrupt", PyChar_interrupt_action, METH_VARARGS,
		   "Interrupts the character's current action.");
  PyChar_addMethod("getAuxiliary", PyChar_get_auxiliary, METH_VARARGS,
		   "get's the specified piece of aux data from the char");
  PyChar_addMethod("cansee", PyChar_cansee, METH_VARARGS,
		   "returns whether or not a char can see an obj or mob.");
  PyChar_addMethod("page", PyChar_page, METH_VARARGS,
		   "page a bunch of text to the character.");
  PyChar_addMethod("isinstance", PyChar_isinstance, METH_VARARGS,
		   "returns whether or not the char inherits from the proto");
  PyChar_addMethod("isInGroup", PyChar_is_in_groups, METH_VARARGS,
		   "returns whether or not the character belongs to one of the groups");

  // add in all the getsetters and methods
  makePyType(&PyChar_Type, pychar_getsetters, pychar_methods);
  deleteListWith(pychar_getsetters, free); pychar_getsetters = NULL;
  deleteListWith(pychar_methods,    free); pychar_methods    = NULL;

  // make sure the room class is ready to be made
  if (PyType_Ready(&PyChar_Type) < 0)
    return;

  // load the module
  m = Py_InitModule3("char", char_module_methods,
		     "The char module, for all char/mob-related MUD stuff.");
  
  // make sure it loaded OK
  if (m == NULL)
    return;

  // add the Char class to the module
  PyTypeObject *type = &PyChar_Type;
  Py_INCREF(&PyChar_Type);
  PyModule_AddObject(m, "Char", (PyObject *)type);
}


int PyChar_Check(PyObject *value) {
  return PyObject_TypeCheck(value, &PyChar_Type);
}

int PyChar_AsUid(PyObject *ch) {
  return ((PyChar *)ch)->uid;
}

CHAR_DATA *PyChar_AsChar(PyObject *ch) {
  return propertyTableGet(mob_table, PyChar_AsUid(ch));
}

PyObject *
newPyChar(CHAR_DATA *ch) {
  PyChar *py_ch = (PyChar *)PyChar_new(&PyChar_Type, NULL, NULL);
  py_ch->uid = charGetUID(ch);
  return (PyObject *)py_ch;
}