nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// items.c
//
// Objects can serve many different functions; they might be containers, they
// might be weapons, they might be pieces of clothing... the list goes on and
// on. The classification of what type of item the object is is what handles
// these sorts of distinctions. This is a general framework for adding new
// item types. Contains functions for storing type data on objects. Item types
// are expected to be able to handle their own subtyping (if neccessary). 
// Objects can be multi-typed (e.g. a desk with a drawer can be both a piece
// of furniture and a container).
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../object.h"
#include "../auxiliary.h"
#include "../storage.h"

#include "items.h"
#include "iedit.h"



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



//*****************************************************************************
// local functions, datastructures, and defines
//*****************************************************************************
// a table of all our item types, and their assocciated new/delete/etc.. funcs
HASHTABLE *type_table = NULL;

#define ITYPE_C   0
#define ITYPE_PY  1

//
// this is what will go into our type_data table
typedef struct item_func_data {
  // what type of item function data do we have?
  int              type;

  // C functions
  void          *(* new)(void);
  void        (* delete)(void *data);
  void         *(* copy)(void *data);
  void        (* copyto)(void *from, void *to);
  STORAGE_SET *(* store)(void *data);
  void         *(* read)(STORAGE_SET *set);

  // Python functions
  PyObject *pyfuncs;

} ITEM_FUNC_DATA;

void *ifunc_new(ITEM_FUNC_DATA *funcs) {
  if(funcs->type == ITYPE_PY) {
    PyObject *ret = PyInstance_New(funcs->pyfuncs, NULL, NULL);
    if(ret == NULL)
      log_pyerr("Error creating new Python item type");
    return ret;
  }
  return funcs->new();
}

void ifunc_delete(ITEM_FUNC_DATA *funcs, void *data) {
  if(funcs->type == ITYPE_PY) {
    Py_XDECREF((PyObject *)data);
  }
  else 
    funcs->delete(data);
}

void *ifunc_copy(ITEM_FUNC_DATA *funcs, void *data) {
  if(funcs->type == ITYPE_PY) {
    PyObject *ret = PyObject_CallMethod(data, "copy", NULL);
    if(ret == NULL)
      log_pyerr("Error copying Python item type");
    return ret;
  }
  return funcs->copy(data);
}

void ifunc_copyto(ITEM_FUNC_DATA *funcs, void *from, void *to) {
  if(funcs->type == ITYPE_PY) {
    PyObject *ret = PyObject_CallMethod(from, "copyTo", "O", to);
    if(ret == NULL)
      log_pyerr("Error calling copyTo Python item type method");
    Py_XDECREF(ret);
  }
  else
    funcs->copyto(from, to);
}

STORAGE_SET *ifunc_store(ITEM_FUNC_DATA *funcs, void *data) {
  if(funcs->type == ITYPE_PY) {
    PyObject  *pyset = PyObject_CallMethod(data, "store", NULL);
    STORAGE_SET *set = NULL;
    if(pyset != NULL)
      set = PyStorageSet_AsSet(pyset);
    else {
      set = new_storage_set();
      log_pyerr("Error storing Python item type");
    }

    Py_XDECREF(pyset);
    return set;
  }
  return funcs->store(data);
}

void *ifunc_read(ITEM_FUNC_DATA *funcs, STORAGE_SET *set) {
  if(funcs->type == ITYPE_PY) {
    PyObject *pyset = newPyStorageSet(set);
    PyObject  *args = Py_BuildValue("(O)", pyset);
    PyObject   *ret = PyInstance_New(funcs->pyfuncs, args, NULL);
    if(ret == NULL)
      log_pyerr("Error reading Python item type");
    Py_XDECREF(args);
    Py_XDECREF(pyset);
    return ret;
  }
  return funcs->read(set);
}

//
// create a new structure for holding item type functions
ITEM_FUNC_DATA *newItemFuncData(void *new, void *delete, void *copyTo,
				void *copy, void *store, void *read) {
  ITEM_FUNC_DATA *data = calloc(1, sizeof(ITEM_FUNC_DATA));
  data->new    = new;
  data->delete = delete;
  data->copy   = copy;
  data->copyto = copyTo;
  data->store  = store;
  data->read   = read;
  data->type   = ITYPE_C;
  return data;
}

//
// create a new structure for holding Python item type functions
ITEM_FUNC_DATA *newPyItemFuncData(PyObject *pyfuncs) {
  ITEM_FUNC_DATA *data = calloc(1, sizeof(ITEM_FUNC_DATA));
  data->pyfuncs = pyfuncs;
  data->type    = ITYPE_PY;
  Py_XINCREF(pyfuncs);
  return data;
}

//
// deletes one structure for holding item type functions
void deleteItemFuncData(ITEM_FUNC_DATA *data) {
  Py_XDECREF(data->pyfuncs);
  free(data);
}

//
// goes through an object's item table and deletes everything in it. Does
// not delete the table itself
void clearItemTable(HASHTABLE *table) {
  // if our hashtable isn't empty, we have to make sure 
  // we delete all of the data within it
  if(hashSize(table) > 0) {
    HASH_ITERATOR *hash_i = newHashIterator(table);
    const char       *key = NULL;
    void             *val = NULL;
    // delete the contents of our hashtable
    ITERATE_HASH(key, val, hash_i) {
      ITEM_FUNC_DATA *funcs = hashGet(type_table, key);
      ifunc_delete(funcs, hashRemove(table, key));
    }
    deleteHashIterator(hash_i);
  }
}

//
// copies the contents of one item table over to another. clears the destination
// table first.
void copyItemTableTo(HASHTABLE *from, HASHTABLE *to) {
  clearItemTable(to);
  // if the table we're copying has contents, go throug hand copy them all
  if(hashSize(from) > 0) {
    HASH_ITERATOR *hash_i = newHashIterator(from);
    const char       *key = NULL;
    void             *val = NULL;
    // copy all the contents
    ITERATE_HASH(key, val, hash_i) {
      ITEM_FUNC_DATA *funcs = hashGet(type_table, key);
      hashPut(to, key, ifunc_copy(funcs, val));
    }
    deleteHashIterator(hash_i);
  }
}

//
// Creates a new item table and copies the contents of the old one 
// over to the new one 
HASHTABLE *copyItemTable(HASHTABLE *table) {
  HASHTABLE *newtable = newHashtable();
  copyItemTableTo(table, newtable);
  return newtable;
}



//*****************************************************************************
// auxiliary data
//*****************************************************************************
typedef struct item_data {
  HASHTABLE *item_table;
} ITEM_DATA;

ITEM_DATA *newItemData() {
  ITEM_DATA *data  = malloc(sizeof(ITEM_DATA));
  data->item_table = newHashtable();
  return data;
}

void deleteItemData(ITEM_DATA *data) {
  clearItemTable(data->item_table);
  deleteHashtable(data->item_table);
  free(data);
}

void itemDataCopyTo(ITEM_DATA *from, ITEM_DATA *to) {
  copyItemTableTo(from->item_table, to->item_table);
}

ITEM_DATA *itemDataCopy(ITEM_DATA *data) {
  ITEM_DATA *newdata = newItemData();
  itemDataCopyTo(data, newdata);
  return newdata;
}

STORAGE_SET *itemDataStore(ITEM_DATA *data) {
  STORAGE_SET       *set = new_storage_set();
  // if we have a type or more, go through them all and store 'em
  if(hashSize(data->item_table) > 0) {
    STORAGE_SET_LIST *list = new_storage_list();
    HASH_ITERATOR  *hash_i = newHashIterator(data->item_table);
    ITEM_FUNC_DATA  *funcs = NULL;
    const char        *key = NULL;
    void              *val = NULL;

    // store all of our item type data
    ITERATE_HASH(key, val, hash_i) {
      STORAGE_SET *one_set = new_storage_set();
      funcs = hashGet(type_table, key);
      store_string(one_set, "type", key);
      store_set(one_set, "data", ifunc_store(funcs, val));
      storage_list_put(list, one_set);
    }
    deleteHashIterator(hash_i);

    store_list(set, "types", list);
  }
  return set;
}


ITEM_DATA *itemDataRead(STORAGE_SET *set) {
  ITEM_DATA         *data = newItemData();
  STORAGE_SET_LIST *types = read_list(set, "types");
  ITEM_FUNC_DATA   *funcs = NULL;
  STORAGE_SET  *one_entry = NULL;

  // go through each entry and parse the item type
  while( (one_entry = storage_list_next(types)) != NULL) {
    const char      *type = read_string(one_entry, "type");
    // make sure the type is valid
    if((funcs = hashGet(type_table, type)) == NULL) 
      continue;
    STORAGE_SET *type_set = read_set(one_entry, "data");
    hashPut(data->item_table, type, ifunc_read(funcs, type_set));
  }
  return data;
}



//*****************************************************************************
// Python implementation
//*****************************************************************************
void item_add_pytype(const char *type, PyObject *pyfuncs) {
  ITEM_FUNC_DATA *funcs = NULL;
  // first, make sure no item type by this name already
  // exists. If one does, delete it so it can be replaced
  if((funcs = hashRemove(type_table, type)) != NULL)
    deleteItemFuncData(funcs);
  funcs = newPyItemFuncData(pyfuncs);
  hashPut(type_table, type, funcs);
}

PyObject *PyMudSys_ItemAddType(PyObject *self, PyObject *args) {
  char        *type = NULL;
  PyObject *pyclass = NULL;

  // parse out our socket and functions
  if(!PyArg_ParseTuple(args, "sO",&type, &pyclass)) {
    PyErr_Format(PyExc_TypeError,"Invalid arguments supplied to " 
		 "item_add_type");
    return NULL;
  }

  // make sure it has a string that says what item type it is
  if(!PyObject_HasAttrString(pyclass, "__item_type__") ||
     !PyString_Check(PyObject_GetAttrString(pyclass, "__item_type__"))) {
    PyErr_Format(PyExc_TypeError,"Python item types must have an attribute, "
		 "__item_type__, that describes what type of item data it is.");
    return NULL;
  }

  // check to make sure it has all of the relevant methods
  //***********
  // FINISH ME
  //***********

  // create the new type
  item_add_pytype(type, pyclass);
  return Py_BuildValue("i", 1);
}

PyObject *PyObj_GetTypeData(PyObject *self, PyObject *args) {
  OBJ_DATA         *obj = PyObj_AsObj(self);
  ITEM_FUNC_DATA *fdata = NULL;
  char            *type = NULL;

  if(!PyArg_ParseTuple(args, "s", &type)) {
    PyErr_Format(PyExc_TypeError, "get_type_data must be supplied a string.");
    return NULL;
  }

  // make sure the object exists
  if(obj == NULL) {
    PyErr_Format(PyExc_TypeError, "Tried to get type data for non-existent object.");
    return NULL;
  }

  // make sure it's Python data
  fdata = hashGet(type_table, type);
  if(fdata == NULL || fdata->type == ITYPE_C)
    return Py_BuildValue("");
  
  // make sure this item actually has the relevant type data
  PyObject *tdata = objGetTypeData(obj, type);
  return Py_BuildValue("O", (tdata == NULL ? Py_None : tdata));
}

PyObject *PyObj_istype(PyObject *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, "istype only accepts strings.");
    return NULL;
  }

  // pull out the object and check the type
  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
  if(obj != NULL)
    return Py_BuildValue("i", objIsType(obj, type));
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to check type of nonexistent object, %d.", 
		 PyObj_AsUid(self));
    return NULL;
  }
}

PyObject *PyObj_get_types(PyObject *self) {  
  // pull out the object and check the type
  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
  if(obj != NULL)
    return Py_BuildValue("s", objGetTypes(obj));
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to list types of nonexistent object, %d.", 
		 PyObj_AsUid(self));
    return NULL;
  }
}

PyObject *PyObj_settype(PyObject *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, "settype only accepts strings.");
    return NULL;
  }

  // pull out the object and check the type
  OBJ_DATA    *obj = PyObj_AsObj((PyObject *)self);
  if(obj != NULL) {
    objSetType(obj, type);
    return Py_BuildValue("i", 1);
  }
  else {
    PyErr_Format(PyExc_StandardError, 
		 "Tried to set type of nonexistent object, %d.", 
		 PyObj_AsUid(self));
    return NULL;
  }
}



//*****************************************************************************
// implementation of items.h
//*****************************************************************************
void init_items(void) {
  type_table = newHashtable();
  auxiliariesInstall("type_data",
		     newAuxiliaryFuncs(AUXILIARY_TYPE_OBJ, newItemData, 
				       deleteItemData, itemDataCopyTo,
				       itemDataCopy, itemDataStore, 
				       itemDataRead));

  // initialize Python mudsys methods
  PyMudSys_addMethod("item_add_type", PyMudSys_ItemAddType, METH_VARARGS,
		     "item_add_type(name, type_data)\n"
		     "\n"
		     "Register a new item type and its data.");

  // initialize Python pyobj methods
  PyObj_addMethod("get_type_data", PyObj_GetTypeData, METH_VARARGS,
    "get_type_data(item_type)\n"
    "\n"
    "Returns Python item type data if it exists, or None.");
  PyObj_addMethod("istype", PyObj_istype, METH_VARARGS,
    "istype(item_type)\n"
    "\n"
    "Returns True or False if the object is of the specified item type.");
  PyObj_addMethod("settype", PyObj_settype, METH_VARARGS,
    "settype(item_type)\n"
    "\n"
    "Make an object be the specified item type.");
  PyObj_addMethod("get_types", PyObj_get_types, METH_NOARGS,
    "get_types()\n"
    "\n"
    "Returns a comma-separated list of item types this object has.");
  // do we need a deltype as well?
  //***********
  // FINISH ME
  //***********

  // initialize item olc
  init_item_olc();

  // now, initialize our basic items types
  extern void init_container(); init_container();
  extern void init_portal();    init_portal();
  extern void init_furniture(); init_furniture();
  extern void init_worn();      init_worn();
}

void item_add_type(const char *type, 
		   void *new,    void *delete,
		   void *copyTo, void *copy, 
		   void *store,  void *read) {
  ITEM_FUNC_DATA *funcs = NULL;
  // first, make sure no item type by this name already
  // exists. If one does, delete it so it can be replaced
  if((funcs = hashRemove(type_table, type)) != NULL)
    deleteItemFuncData(funcs);
  funcs = newItemFuncData(new, delete, copyTo, copy, store, read);
  hashPut(type_table, type, funcs);
}

LIST *itemTypeList(void) {
  LIST *list = newList();
  HASH_ITERATOR *hash_i = newHashIterator(type_table);
  const char       *key = NULL;
  void             *val = NULL;
  ITERATE_HASH(key, val, hash_i)
    listPutWith(list, strdup(key), strcasecmp);
  deleteHashIterator(hash_i);
  return list;
}

void *objGetTypeData(OBJ_DATA *obj, const char *type) {
  ITEM_DATA *data = objGetAuxiliaryData(obj, "type_data");
  return hashGet(data->item_table, type);
}

void objSetType(OBJ_DATA *obj, const char *type) {
  ITEM_FUNC_DATA *funcs = hashGet(type_table, type);
  ITEM_DATA       *data = objGetAuxiliaryData(obj, "type_data");
  void   *old_item_data = hashGet(data->item_table, type);
  // if the type exists and we're not already of the type, set it on us
  if(funcs != NULL && old_item_data == NULL) 
    hashPut(data->item_table, type, ifunc_new(funcs));
}

void objDeleteType(OBJ_DATA *obj, const char *type) {
  ITEM_FUNC_DATA *funcs = hashGet(type_table, type);
  ITEM_DATA *data       = objGetAuxiliaryData(obj, "type_data");
  void *old_item_data   = hashRemove(data->item_table, type);
  if(old_item_data) ifunc_delete(funcs, old_item_data);
}

bool objIsType(OBJ_DATA *obj, const char *type) {
  ITEM_DATA *data = objGetAuxiliaryData(obj, "type_data");
  return hashIn(data->item_table, type);
}

const char *objGetTypes(OBJ_DATA *obj) {
  static char buf[MAX_BUFFER];
  ITEM_DATA *data = objGetAuxiliaryData(obj, "type_data");
  if(hashSize(data->item_table) == 0)
    sprintf(buf, "none");
  else {
    *buf = '\0';
    HASH_ITERATOR *hash_i = newHashIterator(data->item_table);
    const char       *key = NULL;
    void             *val = NULL;
    int             count = 0;

    ITERATE_HASH(key, val, hash_i) {
      sprintf(buf, "%s%s%s", buf, (count > 0 ? ", " : ""), key);
      count++;
    }
    deleteHashIterator(hash_i);
  }
  return buf;
}