//***************************************************************************** // // 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 /* Removed as of v3.3 -- These can now be added via Python with the function, mudsys.add_worn_type(<type>, <position list>) 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"); */ }