/** * @file zone.c * @ingroup zone * * Zone based code * * @author Geoff Davis <geoff@circlemudsquared.org> * @author Greg Buxton <greg@circlemudsquared.org> * * @par Copyright: * Copyright (C) 2006 Geoff Davis <geoff@circlemudsquared.org><br> * Greg Buxton <greg@circlemudsquared.org> * * @par * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br> * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * * @par * All rights reserved. See license.doc for complete information. * * @package cs * @version 1.0 */ #define __ZONE_C__ #include <sys/types.h> #include <dirent.h> #include <string.h> #include "base.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "command.h" #include "constants.h" #include "dao.h" #include "db.h" #include "handler.h" #include "log.h" #include "interpreter.h" #include "spells.h" #include "room.h" #include "item.h" #include "extraDesc.h" #include "mobile.h" #include "zone.h" #include "hashMap.h" #include "memory.h" #include "screen.h" #include "shop.h" /* * External variables. */ extern const char *zone_reset_types[]; /* * Local function prototypes. */ bool zoneData_saveFile(zoneData_t *zone); void zoneData_resetCommandToDao(daoData_t *dao, int n, resetCommand_t *cmd); void zoneDao_readFile(char *filename); void zoneData_fromDao(daoData_t *zoneDao); void resetCommand_fromDao(zoneData_t *zone, daoData_t *resetDao); void zoneData_free(void *z); void zones_linkWorld(void); void zones_resolveResetCommands(void); #define SAVEZONE_USAGE \ "Usage: savezone <zone-keyword>\r\n" \ " savezone all\r\n" /** * @brief The SAVEZONE command. * * This command allows a specific zone to be saved to disk. * * @param ch the character calling the command * @param argument the command line argument passed to the command * @param cmd the index of the corresponding entry in the cmd_info array * @returns none */ ACMD(do_savezone) { char arg[MAX_INPUT_LENGTH] = { '\0' }; /* Read one word from the command line. */ argument = any_one_arg(argument, arg); if (*arg == '\0') { send_to_char(ch, "%s", SAVEZONE_USAGE); } else if (strcasecmp(arg, "all") == 0) { /* Declare an iterator variable. */ hashMapIterator_t *iter = hashMapIterator_create(zones); send_to_char(ch, "Saving zones:\r\n"); /* Iterate each zone in the zone table. */ while(iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); if (zone) { if (!zoneData_saveFile(zone)) { send_to_char(ch, "...Unable to save zone '%s'.\r\n", zone->name); } } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); send_to_char(ch, "...Done.\r\n"); } else { zoneData_t *zone = zoneData_find(arg); if (zone) { if (!zoneData_saveFile(zone)) { send_to_char(ch, "Unable to save zone '%s'.\r\n", zone->name); } else { send_to_char(ch, "Zone '%s' saved.\r\n", zone->name); } } else { send_to_char(ch, "Unable to find a zone '%s'.\r\n", arg); } } } /** * Save a zone to disk in DAO format * @param zone Zone to save * @returns TRUE or FALSE */ bool zoneData_saveFile(zoneData_t *zone) { bool retcode = FALSE; /* Declare a variable to contain the filename. */ char filename[PATH_MAX] = { '\0' }; if (zone == NULL) { log("zoneData_saveFile(): invalid 'zone' zoneData_t."); } else if (!get_filename(filename, sizeof(filename), ZONE_FILE, zone->keyword)) { log("zoneData_saveFile(): couldn't get filename for zone %s.", zone->keyword); } else { /* Declare some iterator variables. */ register int i = 0; hashMapIterator_t *iter = NULL; /* Declare some temporary DAO pointers. */ daoData_t *rootDao = NULL, *subContainerDao = NULL, *zoneDao = NULL; rootDao = dao_newDocument(); zoneDao = dao_newChild(rootDao, "zone"); dao_newScalar(zoneDao, "name", "%s", zone->name); dao_newScalar(zoneDao, "keyword", "%s", zone->keyword); dao_newScalar(zoneDao, "lifespan", "%d", zone->lifespan); if (zone->cmd && zone->cmd->command != ZCMD_STOP) { /* Construct the 'resetList' container DAO. */ subContainerDao = dao_newChild(zoneDao, "resets"); /* Iterate over the zone's reset command list. */ for (i = 0; zone->cmd[i].command != ZCMD_STOP; i++) { /* Convert each zone command to its DAO representation. */ zoneData_resetCommandToDao(subContainerDao, i, &(zone->cmd[i])); } } /* Set the subcontainer DAO back to NULL. */ subContainerDao = NULL; /* Save rooms if any */ if (zone->rooms) { /* Allocate the 'roomList' DAO container */ subContainerDao = dao_newChild(zoneDao, "rooms"); /* Iterate over the rooms in the zone, and save them */ iter = hashMapIterator_create(zone->rooms); while(iter && hashMapIterator_getValue(iter)) { roomData_t *room = (roomData_t *)hashMapIterator_getValue(iter); if (room) { roomData_toDao(subContainerDao, room); } room = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } /* Set the subcontainer DAO back to NULL. */ subContainerDao = NULL; /* Save items if any */ if (zone->items) { subContainerDao = dao_newChild(zoneDao, "itemPrototypes"); /* Iterate over the items in the zone, and save them */ iter = hashMapIterator_create(zone->items); while(iter && hashMapIterator_getValue(iter)) { itemData_t *item = (itemData_t *)hashMapIterator_getValue(iter); if (item) { itemData_toDao(subContainerDao, item); } item = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } /* Set the subcontainer DAO back to NULL. */ subContainerDao = NULL; /* Save mobiles if any */ if (zone->mobiles) { subContainerDao = dao_newChild(zoneDao, "mobilePrototypes"); /* Iterate and save */ iter = hashMapIterator_create(zone->mobiles); while(iter && hashMapIterator_getValue(iter)) { charData_t *mobile = (charData_t *)hashMapIterator_getValue(iter); if (mobile) { mobile_toDao(subContainerDao, mobile); } mobile = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } /* Save shops if any */ if (zone->shops) { subContainerDao = dao_newChild(zoneDao, "shops"); /* Iterate and save */ iter = hashMapIterator_create(zone->shops); while(iter && hashMapIterator_getValue(iter)) { shopData_t *shop = (shopData_t *)hashMapIterator_getValue(iter); if (shop) { shopData_toDao(subContainerDao, shop); } shop = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } /* Save the DAO out to disk. */ retcode = dao_saveFile(rootDao, filename); /* Free the top-level DAO container. */ dao_free(rootDao); } return (retcode); } /** * Handler function to cut down on duplicated code in the zone reset command saving * * @param dao DAO being built * @param key String key value for dao scalar being generated * @param item Item we're saving an entry for * @param sArg String argument value if item pointer is NULL * @return none */ void save_itemCmd(daoData_t *dao, char *key, itemData_t *item, char *sArg) { if (dao == NULL) { log("save_itemCmd(): Invalid daoData_t 'dao'."); } else if (!key || !*key) { log("save_itemCmd(): Invalid char * 'key'."); } else { if (item && item->zone) { dao_newScalar(dao, key, "%s:%d", item->zone->keyword, item->vnum); } else if (sArg && *sArg) { dao_newScalar(dao, key, "%s", sArg); } else { log("save_itemCmd(): NULL pointer and string value. Unable to save."); } } } /** * Handler function to cut down on duplicated code in the zone reset command saving * * @param dao DAO being built * @param key String key value for dao scalar being generated * @param room Room we're saving an entry for * @param sArg String argument value if room pointer is NULL * @return none */ void save_roomCmd(daoData_t *dao, char *key, roomData_t *room, char *sArg) { if (dao == NULL) { log("save_roomCmd(): Invalid daoData_t 'dao'."); } else if (!key || !*key) { log("save_roomCmd(): Invalid char * 'key'."); } else { if (room && room->zone) { dao_newScalar(dao, key, "%s:%d", room->zone->keyword, room->number); } else if (sArg && *sArg) { dao_newScalar(dao, key, "%s", sArg); } else { log("save_roomCmd(): NULL pointer and string value. Unable to save."); } } } /** * Handler function to cut down on duplicated code in the zone reset command saving * * @param dao DAO being built * @param key String key value for dao scalar being generated * @param mobile Mob we're saving an entry for * @param sArg String argument value if mobile pointer is NULL * @return none */ void save_mobCmd(daoData_t *dao, char *key, charData_t *mobile, char *sArg) { if (dao == NULL) { log("save_mobCmd(): Invalid daoData_t 'dao'."); } else if (!key || !*key) { log("save_mobCmd(): Invalid char * 'key'."); } else { if (mobile && mobile->zone) { dao_newScalar(dao, key, "%s:%d", mobile->zone->keyword, mobile->vnum); } else if (sArg && *sArg) { dao_newScalar(dao, key, "%s", sArg); } else { log("save_mobCmd(): NULL pointer and string value. Unable to save."); } } } /** * Convert a zone reset to its DAO representation. * @param parentDao the container DAO to contain the reset's DAO * @param n the index of the zone reset in the zone's reset list * @param cmd the zone reset to be converted * @return none */ void zoneData_resetCommandToDao(daoData_t *dao, int n, resetCommand_t *cmd) { if (dao == NULL) { log("zoneData_resetCommandToDao(): invalid 'dao' daoData_t."); } else if (cmd == NULL) { log("zoneData_resetCommandToDao(): invalid 'cmd' resetCommand_t."); } else { /* Declare some working DAO pointers. */ daoData_t *resetDao = NULL; resetDao = dao_newChild(dao, "%d", n); if (cmd->disabled) dao_newScalar(resetDao, "disabled", "%s", YESNO(cmd->disabled)); if (cmd->if_flag) dao_newScalar(resetDao, "then", "%s", YESNO(cmd->if_flag)); dao_newScalar(resetDao, "command", "%s", reset_command_types[cmd->command]); switch (cmd->command) { case ZCMD_LOADMOB: save_mobCmd(resetDao, "mobile", cmd->mobile, cmd->sArg1); save_roomCmd(resetDao, "inRoom", cmd->inRoom, cmd->sArg3); dao_newScalar(resetDao, "maxNumber", "%d", cmd->maxNum); break; case ZCMD_LOADITEM: save_itemCmd(resetDao, "item", cmd->item, cmd->sArg1); save_roomCmd(resetDao, "inRoom", cmd->inRoom, cmd->sArg3); dao_newScalar(resetDao, "maxNumber", "%d", cmd->maxNum); break; case ZCMD_GIVEITEM: save_itemCmd(resetDao, "item", cmd->item, cmd->sArg1); dao_newScalar(resetDao, "maxNumber", "%d", cmd->maxNum); break; case ZCMD_SETDOOR: save_roomCmd(resetDao, "inRoom", cmd->inRoom, cmd->sArg1); dao_newScalar(resetDao, "direction", "%s", dirs[cmd->direction]); /* We need a save function based on types yet =P */ switch(cmd->state) { case 0: dao_newScalar(resetDao, "state", "open"); break; case 1: dao_newScalar(resetDao, "state", "closed"); break; case 2: dao_newScalar(resetDao, "state", "locked"); break; } break; case ZCMD_PURGEITEM: save_roomCmd(resetDao, "inRoom", cmd->inRoom, cmd->sArg1); save_itemCmd(resetDao, "item", cmd->item, cmd->sArg2); break; case ZCMD_PURGEMOB: save_roomCmd(resetDao, "inRoom", cmd->inRoom, cmd->sArg1); save_mobCmd(resetDao, "mobile", cmd->mobile, cmd->sArg2); break; case ZCMD_EQUIPMOB: save_itemCmd(resetDao, "item", cmd->item, cmd->sArg1); dao_newScalar(resetDao, "wearLocation", "%s", equip_slots[cmd->wearLocation]); dao_newScalar(resetDao, "maxNumber", "%d", cmd->maxNum); break; case ZCMD_PUTITEM: save_itemCmd(resetDao, "item", cmd->item, cmd->sArg1); save_itemCmd(resetDao, "inItem", cmd->item, cmd->sArg2); dao_newScalar(resetDao, "maxNumber", "%d", cmd->maxNum); break; } } } /* ************************************************************************ */ /* Load Zone Data from DAO */ /* ************************************************************************ */ /** * Generate a hash value for the vnum hash. Just return the vnum * * @param v The VNum to be hashed * @return hashKey_t value of the VNum */ hashKey_t hashVnum(const void *v) { hashKey_t hVnum = *((IDXTYPE *)v); return (hVnum); } /** * Generate a hash value for a string. * * @param s the String a hashKey_t is being generated for (void *) * @return The hahsKey_t value for the string */ hashKey_t hashStr(const void *s) { char *string = *((char **)(s)); register int i = 0; hashKey_t hashKey = (hashKey_t) 0; if (string && *string) { /* initialize hash key to a prime number */ hashKey = 17; for (i = 0; string[i]; i++) { hashKey = (hashKey * 37) + (int) string[i]; } } return (hashKey); } /** * Create the zones hashMap_t and then load the zones from disk * * @param none * @return none * * @todo Need to add alternatives to opendir() and readdir() for other OSes. Windows: FindFirstFile/FindNextFile */ void zoneData_loadWorld() { struct dirent *dp = NULL; DIR *dfd = NULL; /* Now we'll get a list of all the files in the directory to load */ /* This is the part that has to have cross-platform alternatives created */ dfd = opendir(ETC_ZONES); if (dfd == NULL) { log("loadZoneMap(): invalid 'dfd' DIR. Aborting."); exit(1); } while((dp = readdir(dfd)) != NULL) { if (strstr(dp->d_name, ".dao")) { zoneDao_readFile(dp->d_name); } } closedir(dfd); /* -- */ /* Link the world */ zones_linkWorld(); } /** * Given a file name, load the zone dao data. * * @param filename filename in ETC_ZONES to load * @return none */ void zoneDao_readFile(char *filename) { if (filename == NULL || *filename == '\0') { log("zoneDao_readFile(): invalid 'filename' char."); } else { char fullFilename[PATH_MAX] = { '\0' }; daoData_t *rootDao = NULL, *zoneDao = NULL; /* Build the full filename */ snprintf(fullFilename, sizeof(fullFilename), "%s%s", ETC_ZONES, filename); /* Load the file into a daoData_t, or exit */ rootDao = dao_readFile(fullFilename); if (rootDao == NULL) { log("zoneDao_readFile(): Unable to load dao data from file '%s'", fullFilename); } /* Get the "zone" group from the daoData_t, or exit */ zoneDao = dao_query(rootDao, "/zone"); if (zoneDao == NULL) { log("zoneDao_readFile(): Unable to find zone dao container in file '%s'", fullFilename); } /* Load the data from the zone daoData_t */ zoneData_fromDao(zoneDao); /* Free the daoData_t */ dao_free(rootDao); } } /** * Given a zone dao entry, load the zone * * @param zoneDao the daoData_t for the zone to load * @return none */ void zoneData_fromDao(daoData_t *zoneDao) { if (zoneDao == NULL) { log("zoneData_fromDao(): Invalid 'zoneDao' daoData_t."); } else { zoneData_t *zone = zoneData_new((char *)dao_queryString(zoneDao, "keyword", NULL)); daoData_t *subDao = NULL; if (zone != NULL) { /* Let's load the zone */ /* Name */ zoneData_setName(zone, (char *)dao_queryString(zoneDao, "name", "(none)")); /* Lifespan */ zone->lifespan = dao_queryInt(zoneDao, "lifespan", -1); /* Reset Commands */ subDao = dao_query(zoneDao, "resets"); resetCommand_fromDao(zone, subDao); subDao = NULL; /* Rooms */ subDao = dao_query(zoneDao, "rooms"); roomData_loadRoomsInZone(zone, subDao); subDao = NULL; /* Items */ subDao = dao_query(zoneDao, "itemPrototypes"); itemData_loadItemsInZone(zone, subDao); subDao = NULL; /* Mobiles */ subDao = dao_query(zoneDao, "mobilePrototypes"); charData_loadMobilesInZone(zone, subDao); subDao = NULL; /* Shops */ subDao = dao_query(zoneDao, "shops"); shopData_loadShopsInZone(zone, subDao); subDao = NULL; /* Done! */ log(" - %-28s (%3d R, %3d I, %3d M, %3d S)", zone->name, zone->rooms ? zone->rooms->size : 0, zone->items ? zone->items->size : 0, zone->mobiles ? zone->mobiles->size : 0, zone->shops ? zone->shops->size : 0 ); } } } /** * Count and return the number of reset commands in the resetList dao * * @param resetDao the daoData_t for the reset commands to count * @return the number of reset commands */ int resetCommand_numInDao(daoData_t *resetDao) { int numEntries = 0; daoData_t *eDao = NULL; for (eDao = resetDao->children; eDao; eDao = eDao->next) { numEntries++; } return (numEntries); } /** * Load the reset commands in the resetList dao * * @param zone The zoneData_t for the zone being loaded * @param resetDao The daoData_t for the reset commands to load * @return none */ void resetCommand_fromDao(zoneData_t *zone, daoData_t *resetDao) { int cmdNum = 0, numTotal = 0; daoData_t *eDao = NULL; if (resetDao == NULL) { /* If there isn't anything then we have to add the 'S' entry */ CREATE(zone->cmd, resetCommand_t, 1); numTotal = 1; } else { numTotal = (resetCommand_numInDao(resetDao) + 1); CREATE(zone->cmd, resetCommand_t, numTotal); for (eDao = resetDao->children; eDao; eDao = eDao->next) { /* First, setup some string pointers */ const char *mobile = NULL, *inRoom = NULL, *item = NULL, *inItem = NULL; /* Let's do some setting for sanity's sake */ zone->cmd[cmdNum].sArg1 = NULL; zone->cmd[cmdNum].sArg2 = NULL; zone->cmd[cmdNum].sArg3 = NULL; zone->cmd[cmdNum].inRoom = NULL; zone->cmd[cmdNum].inItem = NULL; zone->cmd[cmdNum].item = NULL; zone->cmd[cmdNum].mobile = NULL; /* And let's load the command */ zone->cmd[cmdNum].command = dao_queryType(eDao, "command", reset_command_types, -1); if (zone->cmd[cmdNum].command == -1) { /* * We now have a disabled flag instead of changing the command value. * This is done so that when we save the resets we don't lose any that * might have errored. They're preserved, but disabled. */ zone->cmd[cmdNum].disabled = TRUE; } zone->cmd[cmdNum].if_flag = FALSE; if (strcasecmp(dao_queryString(eDao, "then", "NO"), "YES") == 0) { zone->cmd[cmdNum].if_flag = TRUE; } if (strcasecmp(dao_queryString(eDao, "disabled", "NO"), "YES") == 0) { zone->cmd[cmdNum].disabled = TRUE; } else { zone->cmd[cmdNum].disabled = FALSE; } /* Now load the arguments */ switch(zone->cmd[cmdNum].command) { case ZCMD_LOADMOB: /* Read the values */ mobile = dao_queryString(eDao, "mobile", NULL); inRoom = dao_queryString(eDao, "inRoom", NULL); zone->cmd[cmdNum].maxNum = dao_queryInt(eDao, "maxNumber", 0); /* Save the string values to the zone struct for later resolving */ if (mobile) zone->cmd[cmdNum].sArg1 = strdup(mobile); if (inRoom) zone->cmd[cmdNum].sArg3 = strdup(inRoom); /* Sanity */ mobile = NULL; inRoom = NULL; break; case ZCMD_LOADITEM: item = dao_queryString(eDao, "item", NULL); inRoom = dao_queryString(eDao, "inRoom", NULL); zone->cmd[cmdNum].maxNum = dao_queryInt(eDao, "maxNumber", 0); if (item) zone->cmd[cmdNum].sArg1 = strdup(item); if (inRoom) zone->cmd[cmdNum].sArg3 = strdup(inRoom); item = NULL; inRoom = NULL; break; case ZCMD_GIVEITEM: item = dao_queryString(eDao, "item", NULL); zone->cmd[cmdNum].maxNum = dao_queryInt(eDao, "maxNumber", 0); if (item) zone->cmd[cmdNum].sArg1 = strdup(item); item = NULL; break; case ZCMD_SETDOOR: inRoom = dao_queryString(eDao, "inRoom", NULL); zone->cmd[cmdNum].direction = dao_queryType(eDao, "direction", dirs, 0); zone->cmd[cmdNum].state = dao_queryType(eDao, "state", door_states, 0); if (inRoom) zone->cmd[cmdNum].sArg1 = strdup(inRoom); inRoom = NULL; break; case ZCMD_PURGEITEM: inRoom = dao_queryString(eDao, "inRoom", NULL); item = dao_queryString(eDao, "item", NULL); if (inRoom) zone->cmd[cmdNum].sArg1 = strdup(inRoom); if (item) zone->cmd[cmdNum].sArg2 = strdup(item); inRoom = NULL; item = NULL; break; case ZCMD_PURGEMOB: inRoom = dao_queryString(eDao, "inRoom", NULL); mobile = dao_queryString(eDao, "mobile", NULL); if (inRoom) zone->cmd[cmdNum].sArg1 = strdup(inRoom); if (mobile) zone->cmd[cmdNum].sArg2 = strdup(mobile); break; case ZCMD_EQUIPMOB: item = dao_queryString(eDao, "item", NULL); zone->cmd[cmdNum].maxNum = dao_queryInt(eDao, "maxNumber", 0); zone->cmd[cmdNum].wearLocation = dao_queryType(eDao, "wearLocation", equip_slots, 0); if (item) zone->cmd[cmdNum].sArg1 = strdup(item); item = NULL; break; case ZCMD_PUTITEM: item = dao_queryString(eDao, "item", NULL); inItem = dao_queryString(eDao, "inItem", NULL); zone->cmd[cmdNum].wearLocation = dao_queryInt(eDao, "maxNumber", 0); if (item) zone->cmd[cmdNum].sArg1 = strdup(item); if (inItem) zone->cmd[cmdNum].sArg2 = strdup(inItem); item = NULL; inItem = NULL; break; default: log("Unknown reset command: %d", zone->cmd[cmdNum].command); break; } cmdNum++; } } /* And end all off with the stop command */ zone->cmd[cmdNum].command = ZCMD_STOP; zone->cmd[cmdNum].if_flag = FALSE; zone->cmd[cmdNum].disabled = FALSE; zone->cmd[cmdNum].sArg1 = NULL; zone->cmd[cmdNum].sArg2 = NULL; zone->cmd[cmdNum].sArg3 = NULL; zone->cmd[cmdNum].inRoom = NULL; zone->cmd[cmdNum].inItem = NULL; zone->cmd[cmdNum].item = NULL; zone->cmd[cmdNum].mobile = NULL; } /* ************************************************************************ */ /* General room functions */ /* ************************************************************************ */ /** * Create a new zone in the zones global hashMap with keyword zKey * * @param zKey Keyword of the zone to add * @return pointer to zoneData_t for new zone */ zoneData_t *zoneData_new(char *zKey) { extern hashMap_t *zones; zoneData_t *zone = NULL; if (zKey == NULL) { log("zoneData_new(): invalid 'zKey' char: %s", zKey); } else { /* Create the zones hashMap if it isn't set */ if (zones == NULL) { zones = hashMap_create(0, hashStr, NULL, zoneData_free); if (zones == NULL) { log("zoneData_new(): NULL returned from hashMap_create(). Aborting."); exit(1); } } /* Attempt to get a zone with this keyword from the zone */ zone = zoneData_find(zKey); /* If we don't have a zone returned, create one. Otherwise free it out */ if (zone == NULL) { CREATE(zone, zoneData_t, 1); /* Set the Keyword */ zone->keyword = strdup(zKey); /* Since this zone isn't in the hashMap, add it */ hashMap_insert(zones, &zone->keyword, zone); } else { if (zone->name) free(zone->name); if (zone->rooms) hashMap_free(zone->rooms); if (zone->items) hashMap_free(zone->items); if (zone->mobiles) hashMap_free(zone->mobiles); } zone->age = 0; /* Sanity's sake... */ zone->name = NULL; zone->rooms = NULL; zone->items = NULL; zone->mobiles = NULL; } return (zone); } /** * Set a zone's name * * @param zone Zone that's being changed * @param name New name for zone * @return none */ void zoneData_setName(zoneData_t *zone, char *name) { if (zone == NULL) { log("zoneData_setName(): invalid 'zone' zoneData_t."); } else if (name == NULL || *name == '\0') { log("zoneData_setName(): invalid 'name' char."); } else { if (zone->name) free(zone->name); zone->name = strdup(name); } } /** * Free a zone entry * * @param z The Zone to be free()d * @return none */ void zoneData_free(void *z) { zoneData_t *zone = (zoneData_t *)(z); if (zone == NULL) { log("zoneData_free(): invalid 'zone' zoneData_t."); } else { if (zone->name) free(zone->name); if (zone->cmd) free(zone->cmd); if (zone->rooms) hashMap_free(zone->rooms); if (zone->items) hashMap_free(zone->items); if (zone->mobiles) hashMap_free(zone->mobiles); free(zone); zone = NULL; } } /** * Remove and free all rooms in a zone. * * This is called as part of zoneData_free() which is used for the zones hashMap. * * @param zone Zone to be emptied of rooms * @return none */ void zoneData_freeRooms(zoneData_t *zone) { hashMapIterator_t *iter = hashMapIterator_create(zone->rooms); while (iter && hashMapIterator_hasNext(iter)) { roomData_t *room = (roomData_t *)hashMapIterator_nextValue(iter); roomData_free(room); room = NULL; } hashMapIterator_free(iter); } /** * Find a zone by keyword in the zones hashMap * * @param zKey Keyword of the zone to find * @return pointer to zoneData_t for the zone with matching Name */ zoneData_t *zoneData_find(char *zKey) { return (zoneData_t*) hashMap_getValue(zones, &zKey, NULL); } /** * Return the number of number of prototypes in the world * * @param protoType Which type of prototype to return. ZONES, ROOMS, MOBILES, ITEMPS * @return Number of prototypes found */ size_t numPrototypes(int protoType) { size_t retNum = 0; if (zones) { if (protoType == ZONES) { retNum = zones->size; } else { hashMapIterator_t *iter = hashMapIterator_create(zones); while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = hashMapIterator_getValue(iter); if (zone) { switch(protoType) { case ROOMS: if (zone->rooms) retNum += zone->rooms->size; break; case MOBILES: if (zone->mobiles) retNum += zone->mobiles->size; break; case ITEMPS: if (zone->items) retNum += zone->items->size; break; } } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } } return retNum; } /** * Age all zones, and reset any that hit their lifespan * * @param none * @return none * * @todo Replace this with event scheudled resets when the event code is in * */ void zones_ageAll(void) { static int timer = 0; if (((++timer * PULSE_ZONE) / PASSES_PER_SEC) >= 60) { /* one minute has passed */ /* * NOT accurate unless PULSE_ZONE is a multiple of PASSES_PER_SEC or a * factor of 60 */ timer = 0; if (zones) { hashMapIterator_t *iter = hashMapIterator_create(zones); while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); /* Age the zone */ zone->age++; /* Reset if need be */ if (zone->age >= zone->lifespan) { zone->age = 0; zoneData_reset(zone); mudlog(CMP, AUTH_WIZARD, FALSE, "Auto zone rest: %s (%s)", zone->name, zone->keyword); } hashMapIterator_next(iter); } hashMapIterator_free(iter); } } } /** * Reset a zone * * @param zone Zone to reset * @returns none */ void zoneData_reset(zoneData_t *zone) { if (zone == NULL) { log("zoneData_reset(): Invalid zoneData_t 'zone'."); } else { /* Declare some temp vars */ register int i = 0; bool lastSucceeded = FALSE; charData_t *mobile = NULL; itemData_t *item = NULL, *inItem = NULL; for (i = 0; zone->cmd[i].command != ZCMD_STOP; i++) { if (zone->cmd[i].disabled == TRUE) continue; if (zone->cmd[i].if_flag == TRUE && lastSucceeded == FALSE) continue; /* Let's do the command */ switch (zone->cmd[i].command) { case ZCMD_LOADMOB: if ((zone->cmd[i].mobile)->number < zone->cmd[i].maxNum && zone->cmd[i].inRoom && (mobile = read_mobile(zone->cmd[i].mobile))) { char_toRoom(mobile, zone->cmd[i].inRoom); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_LOADITEM: if ((zone->cmd[i].item)->number < zone->cmd[i].maxNum && zone->cmd[i].inRoom && (item = read_item(zone->cmd[i].item))) { itemData_toRoom(item, zone->cmd[i].inRoom); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_GIVEITEM: if (mobile == NULL) { log("zoneData_reset(): Attempt to give item to non-existant mob. Disabling."); zone->cmd[i].disabled = TRUE; lastSucceeded = FALSE; break; } if ((zone->cmd[i].item)->number < zone->cmd[i].maxNum && (item = read_item(zone->cmd[i].item))) { itemData_toChar(item, mobile); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_SETDOOR: if (zone->cmd[i].inRoom && zone->cmd[i].direction >= 0 && zone->cmd[i].direction < NUM_OF_DIRS) { if ((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)] != NULL) { switch(zone->cmd[i].state) { case 0: REMOVE_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_CLOSED); REMOVE_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_LOCKED); break; case 1: SET_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_CLOSED); REMOVE_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_LOCKED); break; case 2: SET_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_CLOSED); SET_BIT((zone->cmd[i].inRoom)->dir[(zone->cmd[i].direction)]->exitInfo, EX_LOCKED); break; } lastSucceeded = TRUE; } else { log("zoneData_reset(): Attempt to set invalid direction in room '%s:%d'. Disabling.", zone->cmd[i].inRoom->zone->keyword, zone->cmd[i].inRoom->number); zone->cmd[i].disabled = TRUE; lastSucceeded = FALSE; break; } } else { lastSucceeded = FALSE; } break; case ZCMD_PURGEITEM: if (zone->cmd[i].inRoom && (item = itemData_getInList((zone->cmd[i].inRoom)->contents, (zone->cmd[i].item)))) { itemData_extract(item); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_PURGEMOB: if (zone->cmd[i].inRoom && (mobile = charData_getMobileInList((zone->cmd[i].inRoom)->people, (zone->cmd[i].mobile)))) { char_extract(mobile); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_EQUIPMOB: if (mobile == NULL) { log("zoneData_reset(): Attempt to equip item on non-existant mob. Disabling."); zone->cmd[i].disabled = TRUE; lastSucceeded = FALSE; break; } if ((zone->cmd[i].item)->number < zone->cmd[i].maxNum && (item = read_item(zone->cmd[i].item)) && zone->cmd[i].wearLocation >= 0 && zone->cmd[i].wearLocation < NUM_WEARS) { char_equipItem(mobile, item, zone->cmd[i].wearLocation); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; case ZCMD_PUTITEM: /* I don't know what to think about this reset command. * I think it should be changed to be a "then" command to work off the last item loaded * instead of the stock behaviour of just finding the first matching item to "inItem" * and using that. */ if (zone->cmd[i].item->number < zone->cmd[i].maxNum && (item = read_item(zone->cmd[i].item)) && (inItem = itemData_getInWorld(zone->cmd[i].inItem))) { itemData_toItem(item, inItem); lastSucceeded = TRUE; } else { lastSucceeded = FALSE; } break; default: log("zoneData_reset(): Unknown zone reset command. Zone '%s' command '%d'", zone->keyword, i); break; } } /* Set the age back to 0 */ zone->age = 0; } } /** * Link the pointers in a zone's reset command list * * This is seperate from the loading from DAO as we have to have all zones * loaded before we can resolve them. It's an extra step, but the end result * is worth it. * * @param zone Zone on which to resolve reset command pointers * @return none */ void zoneData_resolveResetCommands(zoneData_t *zone) { if (zone == NULL) { log("zoneData_resolveResetCommands(): Invalid zoneData_t 'zone'."); } else if (zone->cmd[0].command != ZCMD_STOP) { /* We don't bother logging if a zone has no reset commands */ register int i = 0; /* Loop through the reset commands */ for (i = 0; zone->cmd[i].command != ZCMD_STOP; i++) { switch(zone->cmd[i].command) { case ZCMD_LOADMOB: if (zone->cmd[i].sArg1) zone->cmd[i].mobile = charData_findMobilePrototype(zone->cmd[i].sArg1); if (zone->cmd[i].sArg3) zone->cmd[i].inRoom = roomData_find(zone->cmd[i].sArg3); if (zone->cmd[i].mobile == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve mobile: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } if (zone->cmd[i].inRoom == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve room: %s", zone->cmd[i].sArg3); zone->cmd[i].disabled = TRUE; } break; case ZCMD_LOADITEM: if (zone->cmd[i].sArg1) zone->cmd[i].item = itemData_find(zone->cmd[i].sArg1); if (zone->cmd[i].sArg3) zone->cmd[i].inRoom = roomData_find(zone->cmd[i].sArg3); if (zone->cmd[i].item == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } if (zone->cmd[i].inRoom == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve room: %s", zone->cmd[i].sArg3); zone->cmd[i].disabled = TRUE; } break; case ZCMD_GIVEITEM: if (zone->cmd[i].sArg1) zone->cmd[i].item = itemData_find(zone->cmd[i].sArg1); if (zone->cmd[i].item == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } break; case ZCMD_SETDOOR: if (zone->cmd[i].sArg1) zone->cmd[i].inRoom = roomData_find(zone->cmd[i].sArg1); if (zone->cmd[i].inRoom == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve room: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } break; case ZCMD_PURGEITEM: if (zone->cmd[i].sArg1) zone->cmd[i].inRoom = roomData_find(zone->cmd[i].sArg1); if (zone->cmd[i].sArg2) zone->cmd[i].item = itemData_find(zone->cmd[i].sArg2); if (zone->cmd[i].inRoom == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve room: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } if (zone->cmd[i].item == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg2); zone->cmd[i].disabled = TRUE; } break; case ZCMD_PURGEMOB: if (zone->cmd[i].sArg1) zone->cmd[i].inRoom = roomData_find(zone->cmd[i].sArg1); if (zone->cmd[i].sArg2) zone->cmd[i].mobile = charData_findMobilePrototype(zone->cmd[i].sArg2); if (zone->cmd[i].inRoom == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve room: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } if (zone->cmd[i].mobile == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve mobile: %s", zone->cmd[i].sArg2); zone->cmd[i].disabled = TRUE; } break; case ZCMD_EQUIPMOB: if (zone->cmd[i].sArg1) zone->cmd[i].item = itemData_find(zone->cmd[i].sArg1); if (zone->cmd[i].item == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } break; case ZCMD_PUTITEM: if (zone->cmd[i].sArg1) zone->cmd[i].item = itemData_find(zone->cmd[i].sArg1); if (zone->cmd[i].sArg2) zone->cmd[i].inItem = itemData_find(zone->cmd[i].sArg2); if (zone->cmd[i].item == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg1); zone->cmd[i].disabled = TRUE; } if (zone->cmd[i].inItem == NULL) { log("zoneData_resolveResetCommands(): Unable to resolve item: %s", zone->cmd[i].sArg2); zone->cmd[i].disabled = TRUE; } break; } } } } /** * Pass all zones to zoneData_resolveResetCommands() * * @param none * @return none */ void zones_resolveResetCommands(void) { if (zones) { hashMapIterator_t *iter = hashMapIterator_create(zones); while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); if (zone) { zoneData_resolveResetCommands(zone); } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } } /** * Search through all entities in the world looking for matches * * @param ch Character to display results to * @param type Type of vnum to search for * @param keyword Keyword to match * @return Number of matches found */ int vnum_search(charData_t *ch, int type, char *keyword) { int numFound = 0; char output[MAX_STRING_LENGTH] = { "\0" }; if (zones) { hashMapIterator_t *iter = hashMapIterator_create(zones); snprintf(output, sizeof(output), "Matching Results\r\n----------------\r\n"); while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); if (zone) { hashMapIterator_t *itt = NULL; itemData_t *tItem = NULL; roomData_t *tRoom = NULL; charData_t *tMob = NULL; switch(type) { case ITEMPS: if (zone->items) itt = hashMapIterator_create(zone->items); break; case MOBILES: if (zone->mobiles) itt = hashMapIterator_create(zone->mobiles); break; case ROOMS: if (zone->rooms) itt = hashMapIterator_create(zone->rooms); break; } while (itt && hashMapIterator_getValue(itt)) { switch(type) { case ITEMPS: tItem = (itemData_t *)hashMapIterator_getValue(itt); if (tItem != NULL && isname(keyword, tItem->name)) snprintf(output + strlen(output), sizeof(output), "%5d. [%s%25s%s:%s%-7d%s] %s\r\n", ++numFound, CCCYN(ch, C_NRM), tItem->zone->keyword, CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), tItem->vnum, CCNRM(ch, C_NRM), tItem->shortDescription); break; case MOBILES: tMob = (charData_t *)hashMapIterator_getValue(itt); if (tMob != NULL && isname(keyword, tMob->player.name)) snprintf(output + strlen(output), sizeof(output), "%5d. [%s%25s%s:%s%-7d%s] %s\r\n", ++numFound, CCCYN(ch, C_NRM), tMob->zone->keyword, CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), tMob->vnum, CCNRM(ch, C_NRM), tMob->player.short_descr); break; case ROOMS: tRoom = (roomData_t *)hashMapIterator_getValue(itt); if (tRoom != NULL && isname(keyword, tRoom->name)) snprintf(output + strlen(output), sizeof(output), "%5d. [%s%25s%s:%s%-7d%s] %s\r\n", ++numFound, CCCYN(ch, C_NRM), tRoom->zone->keyword, CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), tRoom->number, CCNRM(ch, C_NRM), tRoom->name); break; } hashMapIterator_next(itt); } if (itt) hashMapIterator_free(itt); } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } if (numFound > 0) { page_string(ch->desc, output, TRUE); } return numFound; } /** * Link exits in a room (room pointers and key pointers) * * @param room Room to link exits in * @return none */ void roomData_linkExits(roomData_t *room) { if (room == NULL) { log("roomData_linkExits(): Invalid roomData_t 'room'."); } else { int i = 0; for (i = 0; i < NUM_OF_DIRS; i++) { if (room->dir[i] == NULL) continue; /* Link the exit if we can */ if (room->dir[i]->toRoomString) { room->dir[i]->toRoom = roomData_find(room->dir[i]->toRoomString); if (room->dir[i]->toRoom == NULL) { log("roomData_linkExits(): Unable to link direction %s in %s:%d to '%s'.", dirs[i], room->zone->keyword, room->number, room->dir[i]->toRoomString); } } /* Set a pointer to the key object for the exit if any, same as above */ if (room->dir[i]->keyString) { room->dir[i]->key = itemData_find(room->dir[i]->keyString); if (room->dir[i]->key == NULL) { log("roomData_linkExits(): Unable to link key '%s' in %s:%d.", room->dir[i]->keyString, room->zone->keyword, room->number); } } } } } /** * Link rooms in a zone (room pointers and key pointers) * * @param zone Zone to link rooms in * @return none */ void zoneData_linkRooms(zoneData_t *zone) { if (zone == NULL) { log("zoneData_linkRooms(): Invalid zoneData_t 'zone'."); } else { if (zone->rooms) { hashMapIterator_t *itt = hashMapIterator_create(zone->rooms); while(itt && hashMapIterator_getValue(itt)) { roomData_t *room = (roomData_t *)hashMapIterator_getValue(itt); if (room) { roomData_linkExits(room); } room = NULL; hashMapIterator_next(itt); } hashMapIterator_free(itt); } } } /** * Link the world * * (Let them know it's Christmas Time...) * * @param none * @return none */ void zones_linkWorld(void) { if (zones) { hashMapIterator_t *iter = hashMapIterator_create(zones); while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); if (zone) { /* Link the zone's reset commands */ zoneData_resolveResetCommands(zone); /* Link the zone's rooms */ zoneData_linkRooms(zone); /* Link the zone's shops */ shopData_resolveInZone(zone); } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } } /** * List world elements to player * * @param ch Character to receive list * @param listType What's being listed */ void world_listToPlayer(charData_t *ch, int listType) { if (ch == NULL) { log("world_listToPlayer(): Invalid charData_t 'ch'."); } else if (zones) { hashMapIterator_t *iter = hashMapIterator_create(zones); char output[MAX_STRING_LENGTH] = { "\0" }; switch(listType) { case SHOPS: snprintf(output, sizeof(output), "Shops\r\n-----\r\n"); break; case DEATHTRAPS: snprintf(output, sizeof(output), "Deathtraps\r\n----------\r\n"); break; case WIZROOMS: snprintf(output, sizeof(output), "Wizrooms\r\n--------\r\n"); break; default: return; } while (iter && hashMapIterator_getValue(iter)) { zoneData_t *zone = (zoneData_t *)hashMapIterator_getValue(iter); if (zone != NULL) { switch(listType) { case SHOPS: shops_listInZone(output, zone); break; case DEATHTRAPS: rooms_listInZone(DEATHTRAPS, output, zone); break; case WIZROOMS: rooms_listInZone(WIZROOMS, output, zone); break; } } zone = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); page_string(ch->desc, output, TRUE); } }