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/
//*****************************************************************************
//
// 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);
}