//*****************************************************************************
//
// 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 *);
HASHTABLE *key_map;
} ZONE_TYPE_DATA;
ZONE_TYPE_DATA *newZoneType(void *reader, void *storer, void *deleter,
void *keysetter) {
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->key_map = newHashtable();
return 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?
HASHTABLE *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, NULL, NULL);
}
}
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 hashGet(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(strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
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 = tdata->read_func(set);
hashPut(tdata->key_map, key, data);
tdata->key_func(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 = tdata->store_func(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)
tdata->key_func(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);
tdata->key_func(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(reader, storer, deleter,
typesetter));
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;
}