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/
//*****************************************************************************
//
// zone.c
//
// A zone is like a stand-alone module, or encounter, within the game (e.g.
// a city in the world), and contains all of the NPCs, objects, rooms, scripts,
// etc... needed for the zone to be complete.
//
//*****************************************************************************

#include <sys/stat.h>
#include <dirent.h>

#include "mud.h"
#include "storage.h"
#include "utils.h"
#include "auxiliary.h"
#include "world.h"
#include "hooks.h"
#include "zone.h"



//*****************************************************************************
// zone type data
//*****************************************************************************
typedef struct {
  /*
  void      *(* read_func)(STORAGE_SET *);
  STORAGE_SET     *(* store_func)(void *);
  void            (* delete_func)(void *);
  void (* key_func)(void *, const char *);
  */
  void    *read_func;
  void   *store_func;
  void  *delete_func;
  void     *key_func;
  bool     forgetful;
  HASHTABLE *key_map;
  char         *type;
} ZONE_TYPE_DATA;

ZONE_TYPE_DATA *newZoneType(const char *type, void *reader, void *storer, 
			    void *deleter, void *keysetter, bool forgetful) {
  ZONE_TYPE_DATA *data = malloc(sizeof(ZONE_TYPE_DATA));
  data->read_func      = reader;
  data->store_func     = storer;
  data->delete_func    = deleter;
  data->key_func       = keysetter;
  data->forgetful      = forgetful;
  data->key_map        = newHashtable();
  data->type           = strdupsafe(type);
  return data;
}

void *do_zone_read(ZONE_TYPE_DATA *tdata, STORAGE_SET *set) {
  if(tdata->forgetful) {
    void *(* read_func)(const char *, STORAGE_SET *) = tdata->read_func;
    return read_func(tdata->type, set);
  }
  else {
    void *(* read_func)(STORAGE_SET *) = tdata->read_func;
    return read_func(set);
  }
}

void do_zone_setkey(ZONE_TYPE_DATA *tdata, void *data, const char *key) {
  if(tdata->forgetful) {
    void (* key_func)(const char *, void *, const char *) = tdata->key_func;
    key_func(tdata->type, data, key);
  }
  else {
    void (* key_func)(void *, const char *) = tdata->key_func;
    key_func(data, key);
  }
}

STORAGE_SET *do_zone_store(ZONE_TYPE_DATA *tdata, void *data) {
  if(tdata->forgetful) {
    STORAGE_SET *(* store_func)(const char *, void *) = tdata->store_func;
    return store_func(tdata->type, data);
  }
  else {
    STORAGE_SET *(* store_func)(void *) = tdata->store_func;
    return store_func(data);
  }
}



//*****************************************************************************
// zone data
//*****************************************************************************
struct zone_data {
  char                   *key;
  char                  *name;
  char               *editors;
  BUFFER                *desc;
  WORLD_DATA           *world;
  LIST            *resettable; // a list of rooms that need to be reset on pulse
  HASHTABLE       *type_table; // a table of our types and their functions
  int             pulse_timer; // the timer duration
  int                   pulse; // how far down have we gone?
  AUX_TABLE   *auxiliary_data; // additional data installed on us
};


ZONE_DATA *newZone(const char *key) {
  ZONE_DATA *zone   = malloc(sizeof(ZONE_DATA));
  zone->type_table  = newHashtable();
  zone->name        = strdup("");
  zone->key         = strdup(key);
  zone->desc        = newBuffer(1);
  zone->editors     = strdup("");
  zone->resettable  = newList();

  zone->pulse_timer    = -1; // never resets
  zone->pulse          = -1;
  zone->world          = NULL;
  zone->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_ZONE);

  return zone;
}

ZONE_DATA *zoneCopy(ZONE_DATA *zone) {
  ZONE_DATA *newzone = newZone(zone->key);
  zoneCopyTo(zone, newzone);
  return newzone;
}

void zoneCopyTo(ZONE_DATA *from, ZONE_DATA *to) {
  zoneSetName(to, zoneGetName(from));
  zoneSetDesc(to, zoneGetDesc(from));
  zoneSetEditors(to, zoneGetEditors(from));
  zoneSetKey(to,   zoneGetKey(from));
  deleteListWith(to->resettable, free);
  to->resettable = listCopyWith(from->resettable, strdup);
  to->pulse_timer = from->pulse_timer;
  to->pulse = from->pulse;
  auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
}

void deleteZone(ZONE_DATA *zone){ 
  if(zone->name)        free(zone->name);
  if(zone->desc)        deleteBuffer(zone->desc);
  if(zone->editors)     free(zone->editors);
  if(zone->type_table)  deleteHashtable(zone->type_table);
  if(zone->key)         free(zone->key);
  if(zone->resettable)  deleteListWith(zone->resettable, free);

  deleteAuxiliaryData(zone->auxiliary_data);

  //*******************************************************************
  // The only time we're deleting a zone is when we're editing the copy
  // in OLC. So, We don't want to delete any of the zone's content when
  // we delete it.
  //*******************************************************************
    
  free(zone);
}


//
// Pulse a zone. i.e. decrement it's reset timer. When the timer hits 0,
// set it back to the max, and reset everything in the zone
void zonePulse(ZONE_DATA *zone) { 
  zone->pulse--;
  if(zone->pulse == 0) {
    zone->pulse = zone->pulse_timer;
    hookRun("reset_zone", hookBuildInfo("str", zoneGetKey(zone)));
  }
}

void zoneForceReset(ZONE_DATA *zone) {
  zone->pulse = 1;
  zonePulse(zone);
}

//
// parses out one resettable room from a storage set
char *read_resettable_room(STORAGE_SET *set) {
  return strdup(read_string(set, "room"));
}

ZONE_DATA *zoneLoad(WORLD_DATA *world, const char *key) {
  ZONE_DATA *zone = newZone(key);
  char fname[SMALL_BUFFER];
  zone->world = world;

  // first, load all of the zone data
  sprintf(fname, "%s/zone", worldGetZonePath(world, zone->key));
  STORAGE_SET  *set = storage_read(fname);
  zone->pulse_timer = read_int   (set, "pulse_timer");
  zoneSetName(zone,   read_string(set, "name"));
  zoneSetDesc(zone,   read_string(set, "desc"));
  zoneSetEditors(zone,read_string(set, "editors"));
  
  // add in all of our resettable rooms
  deleteList(zone->resettable);
  zone->resettable = gen_read_list(read_list(set, "resettable"),
				   read_resettable_room);

  deleteAuxiliaryData(zone->auxiliary_data);
  zone->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
					   AUXILIARY_TYPE_ZONE);
  storage_close(set);

  return zone;
}

//
// turns an entry for a resettable room into a storage set
STORAGE_SET *store_resettable_room(char *key) {
  STORAGE_SET *set = new_storage_set();
  store_string(set, "room", key);
  return set;
}

//
// the new zone saving function
bool zoneSave(ZONE_DATA *zone) {
  char fname[MAX_BUFFER];
  
  // first, for our zone data
  sprintf(fname, "%s/zone", worldGetZonePath(zone->world, zone->key));
  STORAGE_SET *set = new_storage_set();
  store_int   (set, "pulse_timer", zone->pulse_timer);
  store_string(set, "name",        zone->name);
  store_string(set, "desc",        bufferString(zone->desc));
  store_string(set, "editors",     zone->editors);
  store_set   (set, "auxiliary",   auxiliaryDataStore(zone->auxiliary_data));
  store_list  (set, "resettable",  gen_store_list(zone->resettable,
						  store_resettable_room));

  storage_write(set, fname);
  storage_close(set);
  return TRUE;
}



//*****************************************************************************
// get and set functions for zones
//*****************************************************************************
void *zoneGetAuxiliaryData(const ZONE_DATA *zone, char *name) {
  return auxiliaryGet(zone->auxiliary_data, name);
}

int zoneGetPulseTimer(ZONE_DATA *zone) { 
  return zone->pulse_timer;
}

int zoneGetPulse(ZONE_DATA *zone) { 
  return zone->pulse;
}

WORLD_DATA *zoneGetWorld(ZONE_DATA *zone) { 
  return zone->world;
}

const char *zoneGetName(ZONE_DATA *zone) { 
  return zone->name;
}

const char *zoneGetDesc(ZONE_DATA *zone) { 
  return bufferString(zone->desc);
}

const char *zoneGetEditors(ZONE_DATA *zone) {
  return zone->editors;
}

BUFFER *zoneGetDescBuffer(ZONE_DATA *zone) {
  return zone->desc;
}

LIST *zoneGetResettable(ZONE_DATA *zone) {
  return zone->resettable;
}

void zoneSetPulseTimer(ZONE_DATA *zone, int timer) { 
  // if we normally do not reset, change that
  if(zone->pulse_timer < 0)
    zone->pulse = timer;
  zone->pulse_timer = timer;
}

void zoneSetPulse(ZONE_DATA *zone, int pulse_left) { 
  zone->pulse = pulse_left;
}

void zoneSetWorld(ZONE_DATA *zone, WORLD_DATA *world) { 
  zone->world = world;
}

void zoneSetName(ZONE_DATA *zone, const char *name) { 
  if(zone->name) free(zone->name);
  zone->name = strdupsafe(name);
}

void zoneSetDesc(ZONE_DATA *zone, const char *desc) { 
  bufferClear(zone->desc);
  bufferCat(zone->desc, desc);
}

void zoneSetEditors(ZONE_DATA *zone, const char *names) {
  if(zone->editors) free(zone->editors);
  zone->editors = strdupsafe(names);
}



//*****************************************************************************
// the new zone type interface
//*****************************************************************************

//
// returns a list of all the keys in the zone for the specified type. Doesn't
// include the locale in the key. Just the name. List and contents must be
// deleted after use.
LIST *zoneGetTypeKeys(ZONE_DATA *zone, const char *type) {
  LIST *key_list = newList();
  char path[MAX_BUFFER];
  sprintf(path, "%s/%s", worldGetZonePath(zone->world, zone->key), type);
  DIR *dir = opendir(path);
  struct dirent *entry = NULL;
  if(dir != NULL) {
    for(entry = readdir(dir); entry; entry = readdir(dir)) {
      if(!startswith(entry->d_name, "."))
	listPut(key_list, strdup(entry->d_name));
    }
    closedir(dir);
  }
  return key_list;
}

//
// loads the item with the specified key into memory and returns it
void *zoneLoadType(ZONE_DATA *zone, const char *type, const char *key) {
  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
  if(tdata == NULL) 
    return NULL;
  else {
    char buf[MAX_BUFFER];
    void *data = NULL;
    sprintf(buf, "%s/%s/%s", worldGetZonePath(zone->world, zone->key), 
	    type, key);
    STORAGE_SET *set = storage_read(buf);
    if(set != NULL) {
      data = do_zone_read(tdata, set);
      hashPut(tdata->key_map, key, data);
      do_zone_setkey(tdata, data, get_fullkey(key, zone->key));
      storage_close(set);
    }
    return data;
  }
}

void *zoneGetType(ZONE_DATA *zone, const char *type, const char *key) {
  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
  if(tdata == NULL) 
    return NULL;
  else {
    void *data = NULL;
    // if we haven't loaded it into memory yet, do so
    if((data = hashGet(tdata->key_map, key)) == NULL)
      data = zoneLoadType(zone, type, key);
    return data;
  }
}

void zoneSaveType(ZONE_DATA *zone, const char *type, const char *key) {
  void *data = zoneGetType(zone, type, key);
  if(data != NULL) {
    ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
    STORAGE_SET      *set = do_zone_store(tdata, data);
    if(set != NULL) {
      char buf[MAX_BUFFER];
      sprintf(buf,"%s/%s/%s",worldGetZonePath(zone->world,zone->key),type,key);
      storage_write(set, buf);
      storage_close(set);
    }
  }
}

void *zoneRemoveType(ZONE_DATA *zone, const char *type, const char *key) {
  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
  if(tdata == NULL)
    return NULL;
  else {
    // first, delete the file for it
    char buf[MAX_BUFFER];
    sprintf(buf, "%s/%s/%s",worldGetZonePath(zone->world,zone->key),type,key);
    unlink(buf);
    // then remove it from the key map
    void *data = hashRemove(tdata->key_map, key);
    if(data != NULL)
      do_zone_setkey(tdata, data, "");
    return data;
  }
}

void zonePutType(ZONE_DATA *zone, const char *type, const char *key,
		 void *data) {
  ZONE_TYPE_DATA *tdata = hashGet(zone->type_table, type);
  if(tdata != NULL) {
    hashPut(tdata->key_map, key, data);
    do_zone_setkey(tdata, data, get_fullkey(key, zone->key));
  }
}

void zoneAddType(ZONE_DATA *zone, const char *type, void *reader, 
		 void *storer, void *deleter, void *typesetter) {
  if(!hashIn(zone->type_table, type)) {
    hashPut(zone->type_table, type, newZoneType(type, reader, storer, deleter, 
						typesetter, FALSE));
    char buf[MAX_BUFFER];
    sprintf(buf, "%s/%s", worldGetZonePath(zone->world, zone->key), type);
    mkdir(buf, S_IRWXU | S_IRWXG);
  }
}

void zoneAddForgetfulType(ZONE_DATA *zone, const char *type, void *reader, 
			  void *storer, void *deleter, void *typesetter) {
  if(!hashIn(zone->type_table, type)) {
    hashPut(zone->type_table, type, newZoneType(type, reader, storer, deleter, 
						typesetter, TRUE));
    char buf[MAX_BUFFER];
    sprintf(buf, "%s/%s", worldGetZonePath(zone->world, zone->key), type);
    mkdir(buf, S_IRWXU | S_IRWXG);
  }
}

void zoneSetKey(ZONE_DATA *zone, const char *key) {
  if(zone->key) free(zone->key);
  zone->key = strdupsafe(key);
}

const char *zoneGetKey(ZONE_DATA *zone) {
  return zone->key;
}