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/
//*****************************************************************************
//
// worn.c
//
// handles all of the functioning of wearable items. Perhaps this could
// eventually be extended to include armors? I think, perhaps, one of the 
// weirdest things about (most) DIKUs is that worn items and armors are two
// different item types; really, wearable items are just armors that provide
// no armor class. Fusing the two item types into one might be a much more
// fruitful route to take. Or perhaps another route would be to just make 
// another item type called "armor" that only functions if the item is also of
// type "worn"; it calculates armor class/protection/whatnot based on the type
// of worn item the item is.
//
// that said, I'm not going to do it. Well, not for NakedMud anyways; I don't
// want to burden other developers with my conception of what a good way to do
// armor class is. Therefore, if you agree with me, I leave the exercise up to
// you :)
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../storage.h"
#include "../character.h"
#include "../socket.h"
#include "../room.h"
#include "../world.h"
#include "../object.h"
#include "../inform.h"
#include "../handler.h"
#include "../hooks.h"

#include "../olc2/olc.h"
#include "iedit.h"
#include "items.h"
#include "worn.h"



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



//*****************************************************************************
// local functions, variables, datastructures, and defines
//*****************************************************************************
HASHTABLE *worn_table = NULL;

typedef struct worn_entry {
  char *type;
  char *positions;
} WORN_ENTRY;  

WORN_ENTRY *newWornEntry(const char *type, const char *positions) {
  WORN_ENTRY *entry = malloc(sizeof(WORN_ENTRY));
  entry->type      = strdupsafe(type);
  entry->positions = strdupsafe(positions);
  return entry;
}

void deleteWornEntry(WORN_ENTRY *entry) {
  if(entry->positions) free(entry->positions);
  if(entry->type)      free(entry->type);
  free(entry);
}

const char *wornTypeGetPositions(const char *type) {
  WORN_ENTRY *entry = hashGet(worn_table, type);
  return (entry ? entry->positions : "");
}

//
// append information about where the item can be worn
void append_worn_hook(const char *info) {
  OBJ_DATA *obj = NULL;
  CHAR_DATA *ch = NULL;
  hookParseInfo(info, &obj, &ch);

  if(objIsType(obj, "worn")) {
    bprintf(charGetLookBuffer(ch),"When worn, this item covers bodyparts: %s.",
	    wornGetPositions(obj));
  }
}



//*****************************************************************************
// item data for worns
//*****************************************************************************
typedef struct worn_data {
  char *type;
} WORN_DATA;

WORN_DATA *newWornData() {
  WORN_DATA *data = malloc(sizeof(WORN_DATA));
  data->type      = strdup("");
  return data;
}

void deleteWornData(WORN_DATA *data) {
  if(data->type) free(data->type);
  free(data);
}

void wornDataCopyTo(WORN_DATA *from, WORN_DATA *to) {
  if(to->type) free(to->type);
  to->type = strdupsafe(from->type);
}

WORN_DATA *wornDataCopy(WORN_DATA *data) {
  WORN_DATA *new_data = newWornData();
  wornDataCopyTo(data, new_data);
  return new_data;
}

STORAGE_SET *wornDataStore(WORN_DATA *data) {
  STORAGE_SET *set = new_storage_set();
  store_string(set, "type", data->type);
  return set;
}

WORN_DATA *wornDataRead(STORAGE_SET *set) {
  WORN_DATA *data = newWornData();
  data->type = strdup(read_string(set, "type"));
  return data;
}



//*****************************************************************************
// functions for interacting with worns
//*****************************************************************************
const char *wornGetType(OBJ_DATA *obj) {
  WORN_DATA *data = objGetTypeData(obj, "worn");
  return data->type;
}

const char *wornGetPositions(OBJ_DATA *obj) {
  WORN_DATA *data = objGetTypeData(obj, "worn");
  return wornTypeGetPositions(data->type);
}

void wornSetType(OBJ_DATA *obj, const char *type) {
  WORN_DATA *data = objGetTypeData(obj, "worn");
  if(data->type) free(data->type);
  data->type = strdupsafe(type);
}



//*****************************************************************************
// worn olc
//*****************************************************************************
#define IEDIT_WORN_TYPE     1

void iedit_worn_show_types(SOCKET_DATA *sock) {
  // we want to display them all by alphabetical order
  LIST *types = newList();

  HASH_ITERATOR *hash_i = newHashIterator(worn_table);
  const char       *key = NULL;
  WORN_ENTRY       *val = NULL;

  // collect all of the types
  ITERATE_HASH(key, val, hash_i)
    listPutWith(types, strdup(key), strcasecmp);
  deleteHashIterator(hash_i);

  // display all of the types
  LIST_ITERATOR *type_i = newListIterator(types);
  int col = 0;

  text_to_buffer(sock, "{wEditable item types:{g\r\n");
  ITERATE_LIST(key, type_i) {
    col++;
    send_to_socket(sock, "  %-14s%s",
		   key, ((col != 0 && col % 4 == 0) ? "\r\n": "   "));
  }
  deleteListIterator(type_i);
  deleteListWith(types, free);
  if(col % 4 != 0) text_to_buffer(sock, "\r\n");
}


//
// the resedit olc needs these declared
void iedit_worn_menu   (SOCKET_DATA *sock, WORN_DATA *data) {
  send_to_socket(sock, 
		 "{g1) type     : {c%s\r\n"
		 "{g   equips to: {c%s\r\n",
		 data->type, wornTypeGetPositions(data->type));
}

int  iedit_worn_chooser(SOCKET_DATA *sock, WORN_DATA *data, const char *option){
  switch(toupper(*option)) {
  case '1':
    iedit_worn_show_types(sock);
    text_to_buffer(sock, "enter choice: ");
    return IEDIT_WORN_TYPE;
  default:
    return MENU_CHOICE_INVALID;
  }
}

bool iedit_worn_parser (SOCKET_DATA *sock, WORN_DATA *data, int choice, 
			  const char *arg) {
  switch(choice) {
  case IEDIT_WORN_TYPE:
    if(!hashIn(worn_table, arg))
      return FALSE;
    if(data->type) free(data->type);
    data->type = strdup(arg);
    return TRUE;
  default:
    return FALSE;
  }
}

void worn_from_proto(WORN_DATA *worn, BUFFER *buf) {
  const char *code = bufferString(buf);
  char        line[SMALL_BUFFER];
  char       *lptr = line;

  // parse out our worn type
  code = strcpyto(lptr, code, '\n'); 
  while(*lptr != '\"') lptr++;
  lptr++; // kill the leading "
  lptr[next_letter_in(lptr, '\"')] = '\0'; // kill closing "
  if(hashIn(worn_table, lptr)) {
    if(worn->type) free(worn->type);
    worn->type = strdup(lptr);
  }
}

void worn_to_proto(WORN_DATA *worn, BUFFER *buf) {
  bprintf(buf, "me.worn_type = \"%s\"\n", worn->type);
}



//*****************************************************************************
// python extentions
//*****************************************************************************
PyObject *PyObj_getworntype(PyObject *self, void *closure) {
  OBJ_DATA *obj = PyObj_AsObj(self);
  if(obj == NULL)
    return NULL;
  else if(objIsType(obj, "worn"))
    return Py_BuildValue("s", wornGetType(obj));
  else {
    PyErr_Format(PyExc_TypeError, "Can only get worntype for wearable items.");
    return NULL;
  }
}

int PyObj_setworntype(PyObject *self, PyObject *value, void *closure) {
  OBJ_DATA *obj = PyObj_AsObj(self);
  if(obj == NULL) {
    PyErr_Format(PyExc_StandardError, "Tried to set worntype for nonexistent "
		 "clothing, %d", PyObj_AsUid(self));
    return -1;
  }
  else if(!objIsType(obj, "worn")) {
    PyErr_Format(PyExc_TypeError, "Tried to set worntype for non-clothing, %s",
		 objGetClass(obj));
    return -1;
  }

  if(!PyString_Check(value)) {
    PyErr_Format(PyExc_TypeError, "Clothing worntype must be a string.");
    return -1;
  }
  
  if(!hashIn(worn_table, PyString_AsString(value))) {
    PyErr_Format(PyExc_TypeError, "Invalid worn type, %s.", 
		 PyString_AsString(value));
    return -1;
  }

  wornSetType(obj, PyString_AsString(value));
  return 0;
}

PyObject *PyMudSys_AddWornType(PyObject *self, PyObject *args) {
  char *type = NULL;
  char  *pos = NULL;

  if(!PyArg_ParseTuple(args, "ss", &type, &pos)) {
    PyErr_Format(PyExc_TypeError, "add_worn_type requires worn type "
		 "and position list.");
    return NULL;
  }

  worn_add_type(type, pos);
  return Py_BuildValue("i", 1);
}



//*****************************************************************************
// install the worn item type
//*****************************************************************************
void worn_add_type(const char *type, const char *required_positions) {
  WORN_ENTRY *entry = NULL;

  // make sure we don't currently have an entry
  if((entry = hashRemove(worn_table, type)) != NULL)
    deleteWornEntry(entry);
  hashPut(worn_table, type, newWornEntry(type, required_positions));
}


//
// this will need to be called by init_items() in items/items.c
void init_worn(void) {
  worn_table = newHashtable();
  item_add_type("worn", 
		newWornData, deleteWornData,
		wornDataCopyTo, wornDataCopy, 
		wornDataStore, wornDataRead);

  // set up the worn OLC too
  item_add_olc("worn", iedit_worn_menu, iedit_worn_chooser, iedit_worn_parser,
	       worn_from_proto, worn_to_proto);

  // attach our hooks to display worn info on look
  hookAdd("append_obj_desc", append_worn_hook);

  // add our new python get/setters
  PyObj_addGetSetter("worn_type", PyObj_getworntype, PyObj_setworntype,
		     "The type of clothing this wearable item is.");
  PyMudSys_addMethod("add_worn_type", PyMudSys_AddWornType, METH_VARARGS,
		     "Adds a new worn type to the game.");
  
  // add in our basic worn types
  //
  // As of v3.3, worn types can now be added via Python as well as C. People are
  // encouraged to do so, outside of this module. These are left for historical
  // purpposes and backwards compatibility with previous version equipment.
  worn_add_type("shirt",                        "torso");
  worn_add_type("gloves",       "left hand, right hand");
  worn_add_type("left glove",               "left hand");
  worn_add_type("right glove",             "right hand");
  worn_add_type("earrings",                  "ear, ear");
  worn_add_type("earring",                        "ear");
  worn_add_type("ring",                        "finger");
}