//***************************************************************************** // // char_vars.c // // This module allows key/value pairs to be stored on a character. The values // can be strings, integers, or doubles. If a value is called for in the wrong // type (e.g. you're trying to get a string as an integer) the module will // automagically handle the conversion. Variable types default to ints. // //***************************************************************************** #include "../mud.h" #include "../utils.h" #include "../character.h" #include "../storage.h" #include "../auxiliary.h" #include "char_vars.h" //***************************************************************************** // // local functions and datastructurs // //***************************************************************************** // used in storage_sets to keep track of what kind of data we're saving const char *char_var_types[] = { "string", "int", "long", "double" }; typedef struct char_var { char *str_val; int type; } CHAR_VAR; CHAR_VAR *newCharVarString(const char *str) { CHAR_VAR *data = malloc(sizeof(CHAR_VAR)); data->str_val = strdupsafe(str); data->type = CHAR_VAR_STRING; return data; } CHAR_VAR *newCharVarInt(int val) { CHAR_VAR *data = malloc(sizeof(CHAR_VAR)); char str_val[20]; sprintf(str_val, "%d", val); data->str_val = strdup(str_val); data->type = CHAR_VAR_INT; return data; } CHAR_VAR *newCharVarLong(long val) { CHAR_VAR *data = malloc(sizeof(CHAR_VAR)); char str_val[20]; sprintf(str_val, "%ld", val); data->str_val = strdup(str_val); data->type = CHAR_VAR_LONG; return data; } CHAR_VAR *newCharVarDouble(double val) { CHAR_VAR *data = malloc(sizeof(CHAR_VAR)); char str_val[20]; sprintf(str_val, "%lf", val); data->str_val = strdup(str_val); data->type = CHAR_VAR_DOUBLE; return data; } void deleteCharVar(CHAR_VAR *data) { if(data->str_val) free(data->str_val); free(data); } CHAR_VAR *charVarCopy(CHAR_VAR *data) { CHAR_VAR *new_data = malloc(sizeof(CHAR_VAR)); new_data->str_val = strdup(data->str_val); new_data->type = data->type; return new_data; } // // delete a hashtable of char vars // void deleteCharVarTable(HASHTABLE *table) { HASH_ITERATOR *hash_i = newHashIterator(table); const char *key = NULL; CHAR_VAR *val = NULL; ITERATE_HASH(key, val, hash_i) deleteCharVar(val); deleteHashIterator(hash_i); deleteHashtable(table); } //***************************************************************************** // // char_var auxiliary data // //***************************************************************************** typedef struct char_var_aux_data { HASHTABLE *char_vars; } CHAR_VAR_AUX_DATA; CHAR_VAR_AUX_DATA * newCharVarAuxData() { CHAR_VAR_AUX_DATA *data = malloc(sizeof(CHAR_VAR_AUX_DATA)); // Hashtables can take up lots of storage space. Because of this, let's // not create any tables until it's actually needed. This will cut down // on lots of memory usage w.r.t. NPCs who do not use character variables // data->char_vars = newHashtable(); data->char_vars = NULL; return data; } void deleteCharVarAuxData(CHAR_VAR_AUX_DATA *data) { if(data->char_vars) deleteCharVarTable(data->char_vars); free(data); } void charVarAuxDataCopyTo(CHAR_VAR_AUX_DATA *from, CHAR_VAR_AUX_DATA *to) { int from_size = (from->char_vars ? hashSize(from->char_vars) : 0); int to_size = (to->char_vars ? hashSize(to->char_vars) : 0); // clear out our current data if(to_size > 0) { deleteCharVarTable(to->char_vars); to->char_vars = NULL; } // check to see if the "from" table exists if(from_size > 0) { // make sure the "to" table exists if(to->char_vars == NULL) to->char_vars = newHashtable(); // copy everything over HASH_ITERATOR *from_i = newHashIterator(from->char_vars); const char *key = NULL; CHAR_VAR *val = NULL; ITERATE_HASH(key, val, from_i) hashPut(to->char_vars, key, charVarCopy(val)); deleteHashIterator(from_i); } } CHAR_VAR_AUX_DATA * charVarAuxDataCopy(CHAR_VAR_AUX_DATA *data) { CHAR_VAR_AUX_DATA *new_data = newCharVarAuxData(); charVarAuxDataCopyTo(data, new_data); return new_data; } STORAGE_SET *charVarAuxDataStore(CHAR_VAR_AUX_DATA *data) { // first, check if the table even exists if(data->char_vars == NULL || hashSize(data->char_vars) == 0) return new_storage_set(); STORAGE_SET *set = new_storage_set(); HASH_ITERATOR *hash_i = newHashIterator(data->char_vars); STORAGE_SET_LIST *list = new_storage_list(); const char *key = NULL; CHAR_VAR *val = NULL; store_list(set, "variables", list); // iterate across all the entries and add them ITERATE_HASH(key, val, hash_i) { STORAGE_SET *var_set = new_storage_set(); store_string(var_set, "key", key); store_string(var_set, "val", val->str_val); store_string(var_set, "type", char_var_types[val->type]); storage_list_put(list, var_set); } deleteHashIterator(hash_i); return set; } HASHTABLE *variableRead(STORAGE_SET *set) { HASHTABLE *table = newHashtable(); STORAGE_SET_LIST *list = read_list(set, "list"); STORAGE_SET *var = NULL; while( (var = storage_list_next(list)) != NULL) hashPut(table, read_string(var, "key"), (void *)read_int(var, "val")); return table; } CHAR_VAR_AUX_DATA *charVarAuxDataRead(STORAGE_SET *set) { // if the set doesn't contain any entries, don't bother trying to parse if(!storage_contains(set, "variables")) return newCharVarAuxData(); CHAR_VAR_AUX_DATA *data = newCharVarAuxData(); STORAGE_SET_LIST *list = read_list(set, "variables"); STORAGE_SET *var_set = NULL; data->char_vars = newHashtable(); while( (var_set = storage_list_next(list)) != NULL) { const char *var_type = read_string(var_set, "type"); if(!strcasecmp(var_type, "int")) hashPut(data->char_vars, read_string(var_set, "key"), newCharVarInt(read_int(var_set, "val"))); else if(!strcasecmp(var_type, "long")) hashPut(data->char_vars, read_string(var_set, "key"), newCharVarLong(read_long(var_set, "val"))); else if(!strcasecmp(var_type, "double")) hashPut(data->char_vars, read_string(var_set, "key"), newCharVarDouble(read_double(var_set, "val"))); else if(!strcasecmp(var_type, "string")) hashPut(data->char_vars, read_string(var_set, "key"), newCharVarString(read_string(var_set, "val"))); else log_string("ERROR: Tried to read unknown char_var type, %s.", var_type); } return data; } void init_char_vars() { // install char vars on the character datastructure auxiliariesInstall("char_var_aux_data", newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR, newCharVarAuxData, deleteCharVarAuxData, charVarAuxDataCopyTo, charVarAuxDataCopy, charVarAuxDataStore,charVarAuxDataRead)); } //***************************************************************************** // // functions for interacting with char_vars // //***************************************************************************** int charGetVarType(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashGet(data->char_vars, key) : NULL); return (var ? var->type : CHAR_VAR_INT); } int charGetInt(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashGet(data->char_vars, key) : NULL); return (var ? atoi(var->str_val) : 0); } long charGetLong(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashGet(data->char_vars, key) : NULL); return (var ? atol(var->str_val) : 0); } double charGetDouble(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashGet(data->char_vars, key) : NULL); return (var ? atof(var->str_val) : 0); } const char *charGetString(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashGet(data->char_vars, key) : NULL); return (var ? var->str_val : ""); } void charSetInt(CHAR_DATA *ch, const char *key, int val) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *old = (data->char_vars ? hashRemove(data->char_vars, key) : NULL); if(data->char_vars == NULL && val != 0) data->char_vars = newHashtable(); if(old != NULL) deleteCharVar(old); if(val != 0) hashPut(data->char_vars, key, newCharVarInt(val)); } void charSetLong(CHAR_DATA *ch, const char *key, long val) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *old = (data->char_vars ? hashRemove(data->char_vars, key) : NULL); if(data->char_vars == NULL && val != 0) data->char_vars = newHashtable(); if(old != NULL) deleteCharVar(old); if(val != 0) hashPut(data->char_vars, key, newCharVarLong(val)); } void charSetDouble(CHAR_DATA *ch, const char *key, double val) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *old = (data->char_vars ? hashRemove(data->char_vars, key) : NULL); if(data->char_vars == NULL && val != 0) data->char_vars = newHashtable(); if(old != NULL) deleteCharVar(old); if(val != 0) hashPut(data->char_vars, key, newCharVarDouble(val)); } void charSetString(CHAR_DATA *ch, const char *key, const char *val) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *old = (data->char_vars ? hashRemove(data->char_vars, key) : NULL); if(data->char_vars == NULL && *val != '\0') data->char_vars = newHashtable(); if(old != NULL) deleteCharVar(old); if(*val != '\0') hashPut(data->char_vars, key, newCharVarString(val)); } bool charHasVar(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); return (data->char_vars ? hashIn(data->char_vars, key) : FALSE); } void charDeleteVar(CHAR_DATA *ch, const char *key) { CHAR_VAR_AUX_DATA *data = charGetAuxiliaryData(ch, "char_var_aux_data"); CHAR_VAR *var = (data->char_vars ? hashRemove(data->char_vars, key) : NULL); if(var != NULL) deleteCharVar(var); }