affects/
//*****************************************************************************
//
// affect.c
//
// Affects are temporary changes to numericvalues assocciated with a character.
// For instance, a character can have a +10 strength affect. Each affect has a
// duration. Negative-duration affects are permenanant.
//
//*****************************************************************************
#include "../mud.h"
#include "../utils.h"
#include "../storage.h"
#include "../auxiliary.h"
#include "../character.h"
#include "../object.h"
#include "../room.h"
#include "../event.h"
#include "../hooks.h"

#include "affects.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../scripts/scripts.h"
#include "../scripts/pychar.h"
#include "../scripts/pyroom.h"
#include "../scripts/pyobj.h"
#include "../scripts/pymudsys.h"
#include "../scripts/pyplugs.h"



//*****************************************************************************
// local variables
//*****************************************************************************

// tables for holding mappings between affects and modifier functions
HASHTABLE *aff_table = NULL;



//*****************************************************************************
// contains a function for setting affects, and a variable saying which type
// of new value (int or double) it takes in.
//*****************************************************************************
typedef struct {
  void  *func;
  int    type;
  bool python;
} AFF_FUNCTION;

AFF_FUNCTION *newAffFunction(void *func, int type, bool python) {
  AFF_FUNCTION *data = malloc(sizeof(AFF_FUNCTION));
  data->func   = func;
  data->type   = type;
  data->python = python;
  if(python == TRUE)
    Py_INCREF((PyObject *)func);
  return data;
}

// primarily needed for Python
#define AFF_RECV_CHAR     0
#define AFF_RECV_ROOM     1
#define AFF_RECV_OBJ      2

//
// execute the function
void affFunctionExec(AFF_FUNCTION *func, void *data, int recv_type,
		     const char *aff_type, double amnt) {
  if(func->python == FALSE) {
    if(func->type == AFFECT_VAR_INT) {
      void (* mod_func)(void *, const char *, int)    = func->func;
      mod_func(data, aff_type, amnt);
    }
    else if(func->type == AFFECT_VAR_DOUBLE) {
      void (* mod_func)(void *, const char *, double) = func->func;
      mod_func(data, aff_type, amnt);
    }
  }
  else {
    // make our Python form
    PyObject *self = NULL;
    if(recv_type == AFF_RECV_CHAR)
      self = charGetPyFormBorrowed(data);
    else if(recv_type == AFF_RECV_OBJ)
      self = objGetPyFormBorrowed(data);
    else if(recv_type == AFF_RECV_ROOM)
      self = roomGetPyFormBorrowed(data);

    // didn't figure out what we are?
    if(self == NULL)
      return;

    // build our argument list as is appropriate
    PyObject *arglist = NULL;
    if(func->type == AFFECT_VAR_INT)
      arglist = Py_BuildValue("Osi", self, aff_type, (int)amnt);
    else
      arglist = Py_BuildValue("Osd", self, aff_type, amnt);

    // call our function
    PyObject *retval  = PyEval_CallObject(func->func, arglist);

    // check for an error:
    if(retval == NULL) {
      char *tb = getPythonTraceback();
      if(tb != NULL) {
	log_string("Error running python affect modifier, %s:\r\n%s\r\n", 
		   aff_type, tb);
	free(tb);
      }
    }

    // garbage collection
    Py_XDECREF(retval);
    Py_XDECREF(arglist);
  }
}



//*****************************************************************************
// affect modifier data and functions
//*****************************************************************************
typedef struct affect_modifer {
  char *type; // the stat we modify
  double val; // where the modification is placed
} AFF_MODIFIER;

AFF_MODIFIER *newAffModifier(const char *type, double val) {
  AFF_MODIFIER *mod = malloc(sizeof(AFF_MODIFIER));
  mod->type         = strdupsafe(type);
  mod->val          = val;
  return mod;
}

void deleteAffModifier(AFF_MODIFIER *mod) {
  if(mod->type) free(mod->type);
  free(mod);
}

STORAGE_SET *affModifierStore(AFF_MODIFIER *mod) {
  STORAGE_SET *set = new_storage_set();
  store_string(set, "key", mod->type);
  store_double(set, "val", mod->val);
  return set;
}

AFF_MODIFIER *affModifierRead(STORAGE_SET *set) {
  return newAffModifier(read_string(set, "key"), read_double(set, "val"));
}

AFF_MODIFIER *affModifierCopy(AFF_MODIFIER *mod) {
  return newAffModifier(mod->type, mod->val);
}

void affModifierCopyTo(AFF_MODIFIER *from, AFF_MODIFIER *to) {
  if(to->type) free(to->type);
  to->type = strdupsafe(from->type);
  to->val  = from->val;
}

void affectModApply(AFF_MODIFIER *mod, void *thing, int recv_type) {
  AFF_FUNCTION *func = hashGet(aff_table, mod->type);
  affFunctionExec(func, thing, recv_type, mod->type, mod->val);
}

void affectModRemove(AFF_MODIFIER *mod, void *thing, int recv_type) {
  AFF_FUNCTION *func = hashGet(aff_table, mod->type);
  affFunctionExec(func, thing, recv_type, mod->type, -mod->val);
}



//*****************************************************************************
// affect data and functions
//*****************************************************************************
struct affect_data {
  char *name;
  char *type;      // magic, physical, curse, etc... not neccessary but
                   // usable if people want to make the differentiation
  int   duration;
  LIST *modifiers;
};

AFFECT_DATA *newAffect(const char *name, int duration) {
  AFFECT_DATA *aff = malloc(sizeof(AFFECT_DATA));
  aff->name        = strdupsafe(name);
  aff->type        = strdupsafe("");
  aff->duration    = duration;
  aff->modifiers   = newList();
  return aff;
}

void deleteAffect(AFFECT_DATA *aff) {
  if(aff->name)      free(aff->name);
  if(aff->type)      free(aff->type);
  if(aff->modifiers) deleteListWith(aff->modifiers, deleteAffModifier);
  free(aff);
}

STORAGE_SET *affectStore(AFFECT_DATA *aff) {
  STORAGE_SET *set = new_storage_set();
  store_string(set, "name",      aff->name);
  store_string(set, "type",      aff->type);
  store_int   (set, "duration",  aff->duration);
  store_list  (set, "modifiers", 
	       gen_store_list(aff->modifiers, affModifierStore));
  return set;
}

AFFECT_DATA *affectRead(STORAGE_SET *set) {
  AFFECT_DATA *aff = malloc(sizeof(AFFECT_DATA));
  aff->name        = strdupsafe(read_string(set, "name"));
  aff->type        = strdupsafe(read_string(set, "type"));
  aff->duration    = read_int(set, "duration");
  aff->modifiers   = gen_read_list(read_list(set,"modifiers"), affModifierRead);
  return aff;
}

void affectCopyTo(AFFECT_DATA *from, AFFECT_DATA *to) {
  if(to->name)      free(to->name);
  if(to->type)      free(to->type);
  if(to->modifiers) deleteListWith(to->modifiers, deleteAffModifier);
  to->name      = strdupsafe(from->name);
  to->duration  = from->duration;
  to->modifiers = listCopyWith(from->modifiers, affModifierCopy);
}

AFFECT_DATA *affectCopy(AFFECT_DATA *aff) {
  AFFECT_DATA *newaff = malloc(sizeof(AFFECT_DATA));
  newaff->name        = strdupsafe(aff->name);
  newaff->type        = strdupsafe(aff->type);
  newaff->duration    = aff->duration;
  newaff->modifiers   = listCopyWith(aff->modifiers, affModifierCopy);
  return newaff;
}

const char *affectGetName(AFFECT_DATA *affect) {
  return affect->name;
}

const char *affectGetType(AFFECT_DATA *affect) {
  return affect->type;
}

int affectGetDuration(AFFECT_DATA *affect) {
  return affect->duration;
}

void affectAddModifier(AFFECT_DATA *affect, 
		       const char *affect_type, double amount) {
  listPut(affect->modifiers, newAffModifier(affect_type, amount));
}

void affectSetType(AFFECT_DATA *affect, const char *type) {
  if(affect->type) free(affect->type);
  affect->type = strdupsafe(type);
}

void affectApply(AFFECT_DATA *affect, void *thing, int recv_type) {
  LIST_ITERATOR *mod_i = newListIterator(affect->modifiers);
  AFF_MODIFIER    *mod = NULL;
  ITERATE_LIST(mod, mod_i) {
    affectModApply(mod, thing, recv_type);
  } deleteListIterator(mod_i);
}

void affectRemove(AFFECT_DATA *affect, void *thing, int recv_type) {
  LIST_ITERATOR *mod_i = newListIterator(affect->modifiers);
  AFF_MODIFIER    *mod = NULL;
  ITERATE_LIST(mod, mod_i) {
    affectModRemove(mod, thing, recv_type);
  } deleteListIterator(mod_i);
}

int affectNameComp(const char *name, AFFECT_DATA *affect) {
  return strcasecmp(name, affect->name);
}



//*****************************************************************************
// auxiliary data
//*****************************************************************************
typedef struct {
  LIST *affects;
} AFFECT_AUX_DATA;

AFFECT_AUX_DATA *newAffAuxData(void) {
  AFFECT_AUX_DATA *data = malloc(sizeof(AFFECT_AUX_DATA));
  data->affects         = newList();
  return data;
}

void deleteAffAuxData(AFFECT_AUX_DATA *data) {
  if(data->affects) deleteListWith(data->affects, deleteAffect);
  free(data);
}

STORAGE_SET *affAuxDataStore(AFFECT_AUX_DATA *data) {
  STORAGE_SET *set = new_storage_set();
  store_list(set, "affects", gen_store_list(data->affects, affectStore));
  return set;
}

AFFECT_AUX_DATA *affAuxDataRead(STORAGE_SET *set) {
  AFFECT_AUX_DATA *data = malloc(sizeof(AFFECT_AUX_DATA));
  data->affects = gen_read_list(read_list(set, "affects"), affectRead);
  return data;
}

void affAuxDataCopyTo(AFFECT_AUX_DATA *from, AFFECT_AUX_DATA *to) {
  if(to->affects) deleteListWith(to->affects, deleteAffect);
  to->affects = listCopyWith(from->affects, affectCopy);
}

AFFECT_AUX_DATA *affAuxDataCopy(AFFECT_AUX_DATA *affaux) {
  AFFECT_AUX_DATA *data = newAffAuxData();
  affAuxDataCopyTo(affaux, data);
  return data;  
}



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

//
// iterate across all of our characters, objects, and rooms, updating affects
void affect_update(void *self, void *nothing, char *arg) {
  LIST_ITERATOR   *ch_i = newListIterator(mobile_list);
  BUFFER           *buf = newBuffer(1);
  LIST_ITERATOR  *aff_i = NULL;
  AFFECT_DATA      *aff = NULL;
  CHAR_DATA         *ch = NULL;
  AFFECT_AUX_DATA *data = NULL;
  ITERATE_LIST(ch, ch_i) {
    data = charGetAuxiliaryData(ch, "affect_data");
    if(listSize(data->affects) > 0) {
      aff_i = newListIterator(data->affects);
      ITERATE_LIST(aff, aff_i) {
	// ignore permenant affects
	if(aff->duration < 0)
	  continue;
	aff->duration--;
	if(aff->duration <= 0) {
	  listRemove(data->affects, aff);
	  affectRemove(aff, ch, AFF_RECV_CHAR);

	  // run our cancel and then wearoff goes first, or else some of the
	  // blanket resets that are tied to cancel hooks will wipe out
	  // important wearoff information
	  hookRun("affect_wearoff", hookBuildInfo("ch str", ch, aff->name));
	  if(*aff->type) {
	    bufferClear(buf);
	    bprintf(buf, "%s_wearoff", aff->type);
	    hookRun(bufferString(buf), hookBuildInfo("ch str", ch, aff->name));
	  }

	  hookRun("affect_cancel",  hookBuildInfo("ch str", ch, aff->name));
	  if(*aff->type) {
	    bufferClear(buf);
	    bprintf(buf, "%s_cancel", aff->type);
	    hookRun(bufferString(buf), hookBuildInfo("ch str", ch, aff->name));
	  }

	  // delete the affect that was just removed
	  deleteAffect(aff);
	}
      } deleteListIterator(aff_i);
    }
  } deleteListIterator(ch_i);

  // remove all affects for objects and rooms
  //***********
  // FINISH ME
  //***********

  // garbage collection
  deleteBuffer(buf);
}

//
// for internal use only; quick lookup of affects
AFFECT_DATA *charGetAffect(CHAR_DATA *ch, const char *affect) {
  LIST *affs = charGetAffects(ch);
  LIST_ITERATOR *aff_i = newListIterator(affs);
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(!strcasecmp(affectGetName(aff), affect))
      break;
  } deleteListIterator(aff_i);
  return aff;
}



//*****************************************************************************
// character commands
//*****************************************************************************

//
// shows one affect modifier to a character
void show_affect_modifier(CHAR_DATA *ch, AFF_MODIFIER *mod) {
  // display the value differently if it's a double or int
  AFF_FUNCTION *func = hashGet(aff_table, mod->type);
  if(func->type == AFFECT_VAR_DOUBLE)
    send_to_char(ch, "    %s by %.2lf\r\n", mod->type, mod->val);
  else if(func->type == AFFECT_VAR_INT)
    send_to_char(ch, "    %s by %d\r\n",    mod->type, (int)mod->val);
}

//
// show one affect to a character
void show_one_affect(CHAR_DATA *ch, AFFECT_DATA *affect) {
  // print the duration buffer perm. if time < 0. in hms otherwise
  char durbuf[SMALL_BUFFER];
  if(affect->duration <= 0)
    sprintf(durbuf, "perm.");
  else {
    int sec = affect->duration % 60;
    int min = ((affect->duration - sec) / 60);
    sprintf(durbuf, "%3dm%2ds", min, sec);
  }

  // show time in minutes if it's more than 60 seconds. Seconds, otherwise
  send_to_char(ch, "%s) %s%s\r\n", durbuf, affect->name,
	       (listSize(affect->modifiers) > 0 ? " affects:" : ""));
  if(listSize(affect->modifiers) > 0) {
    LIST_ITERATOR *mod_i = newListIterator(affect->modifiers);
    AFF_MODIFIER    *mod = NULL;
    ITERATE_LIST(mod, mod_i) {
      show_affect_modifier(ch, mod);
    } deleteListIterator(mod_i);
  }
}

//
// show all of the affects in the given list to the specified character
void show_affects(CHAR_DATA *ch, LIST *affects) {
  LIST_ITERATOR *aff_i = newListIterator(affects);
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    show_one_affect(ch, aff);
  } deleteListIterator(aff_i);
}

//
// iterates over a character's affect list, and displays them all to him
COMMAND(cmd_affect) {
  AFFECT_AUX_DATA *data = charGetAuxiliaryData(ch, "affect_data");
  if(listSize(data->affects) == 0)
    send_to_char(ch, "You currently have no affects on you.\r\n");
  else {
    send_to_char(ch, "{wCurrent affects:{g\r\n");
    show_affects(ch, data->affects);
    send_to_char(ch, "{n");
  }
}



//*****************************************************************************
// python extentions
//*****************************************************************************
PyObject *PyChar_getaffects(PyObject *self, void *closure) {
  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) 
    return NULL;

  LIST_ITERATOR *aff_i = newListIterator(charGetAffects(ch));
  AFFECT_DATA     *aff = NULL;
  PyObject *list = PyList_New(0);
  ITERATE_LIST(aff, aff_i) {
    PyObject *affname = Py_BuildValue("s", affectGetName(aff));
    PyList_Append(list, affname);
    Py_DECREF(affname);
  } deleteListIterator(aff_i);

  PyObject *retval = Py_BuildValue("O", list);
  Py_DECREF(list);
  return retval;
}

PyObject *PyChar_affected(PyObject *self, PyObject *args) {
  char *name = NULL;
  if(!PyArg_ParseTuple(args, "s", &name)) {
    PyErr_Format(PyExc_TypeError, "affect name must be specified.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }

  return Py_BuildValue("i", charIsAffected(ch, name));
}

PyObject *PyChar_add_affect(PyObject *self, PyObject *args) {
  char   *name = NULL;
  char   *type = NULL;
  int duration = 0;

  if(!PyArg_ParseTuple(args, "si|s", &name, &duration, &type)) {
    PyErr_Format(PyExc_TypeError, "affect name and duration must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  // remove our old affect
  if(charIsAffected(ch, name))
    charRemoveAffect(ch, name);

  // add our new affect
  AFFECT_DATA *affect = newAffect(name, duration);
  if(type != NULL)
    affectSetType(affect, type);
  charAddAffect(ch, affect);
  return Py_BuildValue("i", 1);
}

PyObject *PyChar_mod_affect(PyObject *self, PyObject *args) {
  char  *name = NULL;
  char   *mod = NULL;
  double amnt = 0;

  if(!PyArg_ParseTuple(args, "ssd", &name, &mod, &amnt)) {
    PyErr_Format(PyExc_TypeError, "affect name, mod, and amount must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  // find our old affect
  LIST_ITERATOR *aff_i = newListIterator(charGetAffects(ch));
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(!strcasecmp(affectGetName(aff), name))
      break;
  } deleteListIterator(aff_i);

  // see if the affect exists
  if(aff == NULL)
    return Py_BuildValue("i", 0);
  else {
    // copy the affect
    aff = affectCopy(aff);

    // remove the old affect
    charRemoveAffect(ch, name);

    // add our new modifier
    affectAddModifier(aff, mod, amnt);

    // add our new affect
    charAddAffect(ch, aff);
    return Py_BuildValue("i", 1);
  }
}

PyObject *PyChar_rem_affect(PyObject *self, PyObject *args) {
  char   *name = NULL;

  if(!PyArg_ParseTuple(args, "s", &name)) {
    PyErr_Format(PyExc_TypeError, "affect name must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  // remove the affect and run the appropriate hooks
  if(charIsAffected(ch, name)) {
    AFFECT_DATA *aff = charGetAffect(ch, name);
    BUFFER      *buf = newBuffer(1);
    hookRun("affect_cancel", hookBuildInfo("ch str", ch, name));
    if(*aff->type) {
      bprintf(buf, "%s_cancel", aff->type);
      hookRun(bufferString(buf), hookBuildInfo("ch str", ch, name));
    }
    charRemoveAffect(ch, name);

    // garbage collection
    deleteBuffer(buf);
  }
  return Py_BuildValue("i", 1);
}

PyObject *PyChar_aff_duration(PyObject *self, PyObject *args) {
  char   *name = NULL;

  if(!PyArg_ParseTuple(args, "s", &name)) {
    PyErr_Format(PyExc_TypeError, "affect name must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  // find the affect
  LIST_ITERATOR *aff_i = newListIterator(charGetAffects(ch));
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(!strcasecmp(affectGetName(aff), name))
      break;
  } deleteListIterator(aff_i);

  if(aff != NULL)
    return Py_BuildValue("i", affectGetDuration(aff));
  return Py_BuildValue("i", 0);
}

PyObject *PyChar_aff_type(PyObject *self, PyObject *args) {
  char   *name = NULL;

  if(!PyArg_ParseTuple(args, "s", &name)) {
    PyErr_Format(PyExc_TypeError, "affect name must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  // find the affect
  LIST_ITERATOR *aff_i = newListIterator(charGetAffects(ch));
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(!strcasecmp(affectGetName(aff), name))
      break;
  } deleteListIterator(aff_i);

  if(aff != NULL)
    return Py_BuildValue("s", affectGetType(aff));
  return Py_BuildValue("O", Py_None);
}

PyObject *PyChar_aff_refresh(PyObject *self, PyObject *args) {
  char   *name = NULL;
  int duration = 0;

  if(!PyArg_ParseTuple(args, "si", &name, &duration)) {
    PyErr_Format(PyExc_TypeError, "affect name and duration must be supplied.");
    return NULL;
  }

  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }
  
  if(!charIsAffected(ch, name))
    return Py_BuildValue("i", 0);

  // find the affect
  LIST_ITERATOR *aff_i = newListIterator(charGetAffects(ch));
  AFFECT_DATA     *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(!strcasecmp(affectGetName(aff), name))
      aff->duration = duration;
  } deleteListIterator(aff_i);

  return Py_BuildValue("i", 1);
}

PyObject *PyChar_aff_has_mod(PyObject *self, PyObject *args) {
  char  *name = NULL;
  char  *type = NULL;
  double amnt = 0;

  if(!PyArg_ParseTuple(args, "ss", &name, &type)) {
    PyErr_Format(PyExc_TypeError, "Affect name and mod type must be supplied.");
    return NULL;
  }

  // make sure we exist
  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }

  // get the affect on the character
  AFFECT_DATA *aff = charGetAffect(ch, name);
  if(aff == NULL)
    return 0;
  LIST_ITERATOR *mod_i = newListIterator(aff->modifiers);
  AFF_MODIFIER    *mod = NULL;
  ITERATE_LIST(mod, mod_i) {
    if(!strcasecmp(mod->type, type)) {
      amnt = mod->val;
      break;
    }
  } deleteListIterator(mod_i);

  return Py_BuildValue("i", (mod == 0 ? 0 : (mod < 0 ? -1 : 1)));
}

PyObject *PyChar_add_modifier(PyObject *self, PyObject *args) {
  char *type = NULL;
  double val = 0;

  if(!PyArg_ParseTuple(args, "sd", &type, &val)) {
    PyErr_Format(PyExc_TypeError, "Affect type and amount to modify must be supplied.");
    return NULL;
  }

  // make sure we exist
  CHAR_DATA *ch = PyChar_AsChar(self);
  if(ch == NULL) {
    PyErr_Format(PyExc_StandardError, "character does not exist.");
    return NULL;
  }

  charAddModifier(ch, type, val);
  return Py_BuildValue("i", 1);
}

PyObject *PyRoom_add_modifier(PyObject *self, PyObject *args) {
  char *type = NULL;
  double val = 0;

  if(!PyArg_ParseTuple(args, "sd", &type, &val)) {
    PyErr_Format(PyExc_TypeError, "Affect type and amount to modify must be supplied.");
    return NULL;
  }

  // make sure we exist
  ROOM_DATA *room = PyRoom_AsRoom(self);
  if(room == NULL) {
    PyErr_Format(PyExc_StandardError, "room does not exist.");
    return NULL;
  }

  roomAddModifier(room, type, val);
  return Py_BuildValue("i", 1);
}

PyObject *PyObj_add_modifier(PyObject *self, PyObject *args) {
  char *type = NULL;
  double val = 0;

  if(!PyArg_ParseTuple(args, "sd", &type, &val)) {
    PyErr_Format(PyExc_TypeError, "Affect type and amount to modify must be supplied.");
    return NULL;
  }

  // make sure we exist
  OBJ_DATA *obj = PyObj_AsObj(self);
  if(obj == NULL) {
    PyErr_Format(PyExc_StandardError, "object does not exist.");
    return NULL;
  }

  objAddModifier(obj, type, val);
  return Py_BuildValue("i", 1);
}

//
// adds a new affect type via Python
PyObject *PyMudSys_AddAffectType(PyObject *self, PyObject *args) {
  char     *name = NULL;
  char  *vartype = NULL;
  PyObject *func = NULL;
  int    var_val = -1;

  if(!PyArg_ParseTuple(args, "ssO", &name, &vartype, &func)) {
    PyErr_Format(PyExc_TypeError, "Affect name, type, and add/remove function must be supplied.");
    return NULL;
  }

  if(!strcasecmp(vartype, "int"))
    var_val = AFFECT_VAR_INT;
  else if(!strcasecmp(vartype, "double"))
    var_val = AFFECT_VAR_DOUBLE;
  else {
    PyErr_Format(PyExc_TypeError, "affect type must be an 'int' or 'double'.");
    return NULL;
  }

  // hash our function
  add_py_affect_type(name, var_val, func);
  return Py_BuildValue("i", 1);
}



//*****************************************************************************
// implementation of affect.h
//*****************************************************************************
void init_affects(void) {
  aff_table = newHashtable();

  auxiliariesInstall("affect_data",
		     newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR| AUXILIARY_TYPE_ROOM|
				       AUXILIARY_TYPE_OBJ,
				       newAffAuxData,    deleteAffAuxData,
				       affAuxDataCopyTo, affAuxDataCopy,
				       affAuxDataStore,  affAuxDataRead));

  // add python extentions
  PyChar_addGetSetter("affects", PyChar_getaffects, NULL, NULL); 
  PyChar_addMethod("affected",     PyChar_affected,     METH_VARARGS, NULL);
  PyChar_addMethod("add_affect",   PyChar_add_affect,   METH_VARARGS, NULL);
  PyChar_addMethod("mod_affect",   PyChar_mod_affect,   METH_VARARGS, NULL);
  PyChar_addMethod("rem_affect",   PyChar_rem_affect,   METH_VARARGS, NULL);
  PyChar_addMethod("aff_duration", PyChar_aff_duration, METH_VARARGS, NULL);
  PyChar_addMethod("aff_type",     PyChar_aff_type,     METH_VARARGS, NULL);
  PyChar_addMethod("aff_refresh",  PyChar_aff_refresh,  METH_VARARGS, NULL);
  PyChar_addMethod("add_modifier", PyChar_add_modifier, METH_VARARGS, NULL);
  PyChar_addMethod("aff_has_mod",  PyChar_aff_has_mod,  METH_VARARGS, NULL);
  PyRoom_addMethod("add_modifier", PyRoom_add_modifier, METH_VARARGS, NULL);
  PyObj_addMethod ("add_modifier", PyObj_add_modifier,  METH_VARARGS, NULL);
  PyMudSys_addMethod("add_affect_type", PyMudSys_AddAffectType, 
		     METH_VARARGS, NULL);

  add_cmd("affects", NULL, cmd_affect, "player", FALSE);

  start_update(NULL, 1 SECOND, affect_update, NULL, NULL, NULL);
}

void add_py_affect_type(const char *affect_type, int vartype, void *addremove) {
  hashPut(aff_table, affect_type, newAffFunction(addremove, vartype, TRUE));
}

void add_affect_type(const char *affect_type, int vartype, void *addremove) {
  hashPut(aff_table, affect_type, newAffFunction(addremove, vartype, FALSE));
}

void charAddAffect(CHAR_DATA *ch, AFFECT_DATA *affect) {
  AFFECT_AUX_DATA *data = charGetAuxiliaryData(ch, "affect_data");
  listPut(data->affects, affect);
  affectApply(affect, ch, AFF_RECV_CHAR);
}

void roomAddAffect(ROOM_DATA *room, AFFECT_DATA *affect) {
  AFFECT_AUX_DATA *data = roomGetAuxiliaryData(room, "affect_data");
  listPut(data->affects, affect);
  affectApply(affect, room, AFF_RECV_ROOM);
}

void objAddAffect(OBJ_DATA  *obj, AFFECT_DATA *affect) {
  AFFECT_AUX_DATA *data = objGetAuxiliaryData(obj, "affect_data");
  listPut(data->affects, affect);
  affectApply(affect, obj, AFF_RECV_OBJ);
}

void charRemoveAffect(CHAR_DATA *ch, const char *affect) {
  AFFECT_AUX_DATA *data = charGetAuxiliaryData(ch, "affect_data");
  AFFECT_DATA      *aff = listRemoveWith(data->affects, affect, affectNameComp);
  affectRemove(aff, ch, AFF_RECV_CHAR);
  deleteAffect(aff);
}

void roomRemoveAffect(ROOM_DATA *room, const char *affect) {
  AFFECT_AUX_DATA *data = roomGetAuxiliaryData(room, "affect_data");
  AFFECT_DATA      *aff = listRemoveWith(data->affects, affect, affectNameComp);
  affectRemove(aff, room, AFF_RECV_ROOM);
  deleteAffect(aff);
}

void objRemoveAffect(OBJ_DATA *obj,  const char *affect) {
  AFFECT_AUX_DATA *data = objGetAuxiliaryData(obj, "affect_data");
  AFFECT_DATA      *aff = listRemoveWith(data->affects, affect, affectNameComp);
  affectRemove(aff, obj, AFF_RECV_OBJ);
  deleteAffect(aff);
}

bool charIsAffected(CHAR_DATA *ch, const char *affect) {
  AFFECT_AUX_DATA *data = charGetAuxiliaryData(ch, "affect_data");
  return (listGetWith(data->affects, affect, affectNameComp) != NULL);
}

bool objIsAffected(OBJ_DATA  *obj,  const char *affect) {
  AFFECT_AUX_DATA *data = objGetAuxiliaryData(obj, "affect_data");
  return (listGetWith(data->affects, affect, affectNameComp) != NULL);
}

bool roomIsAffected(ROOM_DATA *room, const char *affect) {
  AFFECT_AUX_DATA *data = roomGetAuxiliaryData(room, "affect_data");
  return (listGetWith(data->affects, affect, affectNameComp) != NULL);
}

LIST *charGetAffects(CHAR_DATA *ch) {
  AFFECT_AUX_DATA *data = charGetAuxiliaryData(ch, "affect_data");
  return data->affects;
}

LIST *roomGetAffects(ROOM_DATA *room) {
  AFFECT_AUX_DATA *data = roomGetAuxiliaryData(room, "affect_data");
  return data->affects;
}

LIST *objGetAffects(OBJ_DATA  *obj) {
  AFFECT_AUX_DATA *data = objGetAuxiliaryData(obj, "affect_data");
  return data->affects;
}

void anyUnaffect(void *thing, int recv_type, AFFECT_AUX_DATA *data, 
		 bool remove_permenants) {
  LIST_ITERATOR  *aff_i = newListIterator(data->affects);
  AFFECT_DATA      *aff = NULL;
  ITERATE_LIST(aff, aff_i) {
    if(aff->duration < 0 && !remove_permenants)
      continue;
    listRemove(data->affects, aff);
    affectRemove(aff, thing, recv_type);
    deleteAffect(aff);
  } deleteListIterator(aff_i);
}

void charUnaffect(CHAR_DATA *ch, bool remove_permenants) {
  anyUnaffect(ch, AFF_RECV_CHAR, charGetAuxiliaryData(ch, "affect_data"), 
	      remove_permenants);
}

void roomUnaffect(ROOM_DATA *room, bool remove_permenants) {
  anyUnaffect(room, AFF_RECV_ROOM, roomGetAuxiliaryData(room,"affect_data"), 
	      remove_permenants);
}

void objUnaffect(OBJ_DATA  *obj,  bool remove_permenants) {
  anyUnaffect(obj, AFF_RECV_OBJ, objGetAuxiliaryData(obj, "affect_data"), 
	      remove_permenants);
}

void charAddModifier(CHAR_DATA *ch,  const char *affect_type, double amount) {
  AFF_FUNCTION *func = hashGet(aff_table, affect_type);
  if(func != NULL) affFunctionExec(func, ch, AFF_RECV_CHAR,affect_type, amount);
}

void roomAddModifier(ROOM_DATA *room,const char *affect_type, double amount) {
  AFF_FUNCTION *func = hashGet(aff_table, affect_type);
  if(func != NULL) affFunctionExec(func, room,AFF_RECV_ROOM,affect_type,amount);
}

void objAddModifier (OBJ_DATA  *obj, const char *affect_type, double amount) {
  AFF_FUNCTION *func = hashGet(aff_table, affect_type);
  if(func != NULL) affFunctionExec(func, obj, AFF_RECV_OBJ,affect_type, amount);
}