/** * @file room.c * @ingroup zone * * Room 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 __ROOM_C__ #include "base.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "constants.h" #include "dao.h" #include "db.h" #include "handler.h" #include "log.h" #include "interpreter.h" #include "spells.h" #include "extraDesc.h" #include "room.h" #include "zone.h" #include "character.h" /* * Local functions */ void roomData_emptyPeople(roomData_t *room); void roomData_emptyContents(roomData_t *room); void roomExitData_free(roomExitData_t *roomExit); void roomExitData_loadRoomExits(roomData_t *room, daoData_t *roomExitDao); void roomData_addRoomFromDao(zoneData_t *zone, daoData_t *roomDao); /** * Convert a room exit to its DAO representation. * @param parentDao the container DAO to contain the exit's DAO * @param exit the room exit to be converted * @return none */ void roomExitData_toDao(daoData_t *parentDao, int dir, roomExitData_t *exit) { if (parentDao == NULL) { log("roomExitData_toDao(): invalid 'parentDao' daoData_t."); } else if (dir < 0 || dir >= NUM_OF_DIRS) { log("roomExitData_toDao(): invalid 'dir' value %d.", dir); } else if (exit == NULL) { log("roomExitData_toDao(): invalid 'exit' roomExitData_t."); } else { /* Declare some working DAO pointers. */ daoData_t *exitDao = NULL, *subContainerDao = NULL; /* Create a container for the room exit. */ exitDao = dao_newChild(parentDao, dirs[dir]); if (exit->generalDescription && *(exit->generalDescription) != '\0') { /* Create the description scalar DAO. */ dao_newScalar(exitDao, "description", "%s", exit->generalDescription); } if (exit->toRoomString != NULL) { /* Create the destination room scalar DAO. */ dao_newScalar(exitDao, "destinationRoom", "%s", exit->toRoomString); } if (exit->exitInfo != 0) { /* Create the exit flags scalar DAO. */ subContainerDao = dao_newChild(exitDao, "flags"); dao_newScalar(subContainerDao, "closed", "%s", YESNO(NEWEXIT_FLAGGED(exit, EX_CLOSED))); dao_newScalar(subContainerDao, "door", "%s", YESNO(NEWEXIT_FLAGGED(exit, EX_ISDOOR))); dao_newScalar(subContainerDao, "locked", "%s", YESNO(NEWEXIT_FLAGGED(exit, EX_LOCKED))); dao_newScalar(subContainerDao, "pickproof", "%s", YESNO(NEWEXIT_FLAGGED(exit, EX_PICKPROOF))); } if (exit->keyString != NULL) { /* Create the key vnum scalar DAO. */ dao_newScalar(exitDao, "key", "%s", exit->keyString); } if (exit->keyword && *(exit->keyword) != '\0') { /* Create the exit keywords scalar DAO. */ dao_newScalar(exitDao, "keywords", "%s", exit->keyword); } } } /** * Convert a room to its DAO representation. * @param parentDao the container DAO to contain the room's DAO * @param room the room to be converted * @return none */ void roomData_toDao(daoData_t *parentDao, roomData_t *room) { if (parentDao == NULL) { log("roomData_toDao(): invalid 'parentDao' daoData_t."); } else if (room == NULL) { log("roomData_toDao(): invalid 'room' roomData_t."); } else { char temp[MAX_INPUT_LENGTH]; /* Declare an iterator variable. */ register int n = 0; /* Declare some working DAO pointers. */ daoData_t *roomDao = NULL, *subContainerDao = NULL; roomDao = dao_newChild(parentDao, "%d", room->number); dao_newScalar(roomDao, "name", "%s", room->name); dao_newScalar(roomDao, "description", "%s", room->description); sprinttype(room->sectorType, sector_types, temp, sizeof(temp)); dao_newScalar(roomDao, "sectorType", "%s", temp); if (room->roomFlags != 0UL) { subContainerDao = dao_newChild(roomDao, "flags"); dao_newScalar(subContainerDao, "dark", "%s", YESNO(IS_SET(room->roomFlags, ROOM_DARK))); dao_newScalar(subContainerDao, "death", "%s", YESNO(IS_SET(room->roomFlags, ROOM_DEATH))); dao_newScalar(subContainerDao, "noMob", "%s", YESNO(IS_SET(room->roomFlags, ROOM_NOMOB))); dao_newScalar(subContainerDao, "indoors", "%s", YESNO(IS_SET(room->roomFlags, ROOM_INDOORS))); dao_newScalar(subContainerDao, "peaceful", "%s", YESNO(IS_SET(room->roomFlags, ROOM_PEACEFUL))); dao_newScalar(subContainerDao, "soundproof", "%s", YESNO(IS_SET(room->roomFlags, ROOM_SOUNDPROOF))); dao_newScalar(subContainerDao, "noTrack", "%s", YESNO(IS_SET(room->roomFlags, ROOM_NOTRACK))); dao_newScalar(subContainerDao, "noMagic", "%s", YESNO(IS_SET(room->roomFlags, ROOM_NOMAGIC))); dao_newScalar(subContainerDao, "tunnel", "%s", YESNO(IS_SET(room->roomFlags, ROOM_TUNNEL))); dao_newScalar(subContainerDao, "private", "%s", YESNO(IS_SET(room->roomFlags, ROOM_PRIVATE))); dao_newScalar(subContainerDao, "wizRoom", "%s", YESNO(IS_SET(room->roomFlags, ROOM_WIZROOM))); dao_newScalar(subContainerDao, "house", "%s", YESNO(IS_SET(room->roomFlags, ROOM_HOUSE))); dao_newScalar(subContainerDao, "houseCrash", "%s", YESNO(IS_SET(room->roomFlags, ROOM_HOUSE_CRASH))); dao_newScalar(subContainerDao, "atrium", "%s", YESNO(IS_SET(room->roomFlags, ROOM_ATRIUM))); dao_newScalar(subContainerDao, "olc", "%s", YESNO(IS_SET(room->roomFlags, ROOM_OLC))); } /* Save the extraDescriptions if there are any */ if (room->exDescription != NULL) { extraDescData_listToDao(roomDao, room->exDescription); } /* Reset the subcontainer DAO to NULL. */ subContainerDao = NULL; /* Iterate over the exit array. */ for (n = 0; n < NUM_OF_DIRS; n++) { /* Skip directions for which there is no option. */ if (room->dir[n] == NULL) { continue; } if (subContainerDao == NULL) { /* Allocate the 'exits' container DAO, if necessary. */ subContainerDao = dao_newChild(roomDao, "exits"); } /* Convert each exit to its DAO representation. */ roomExitData_toDao(subContainerDao, n, room->dir[n]); } } } /* ************************************************************************ */ /* Load Room Data from DAO */ /* ************************************************************************ */ /** * Create a room hashMap * * @param none * @return pointer to hashMap that's created */ hashMap_t *roomData_createHashMap() { hashMap_t *roomHash = NULL; roomHash = hashMap_create(0, hashVnum, NULL, roomData_free); if (roomHash == NULL) { log("roomData_createHashMap(): NULL returned from hashMap_create()"); exit(1); } return (roomHash); } /** * Load rooms from the rooms dao, adds them to a hashMap * * param zone Zone rooms are being loaded into * @param roomListDao daoData_t of the rooms to load * @return none */ void roomData_loadRoomsInZone(zoneData_t *zone, daoData_t *roomListDao) { if (zone == NULL) { log("roomData_loadRoomsInZone(): Invalid 'zone' zoneData_t."); } else if (roomListDao == NULL) { log("roomData_loadRoomsInZone(): Invalid 'roomListDao' daoData_t."); } else { daoData_t *eDao = NULL; /* Create the zone's room hashMap if needed */ if (zone->rooms == NULL) { zone->rooms = roomData_createHashMap(); } /* Add all rooms in the roomList dao */ for (eDao = roomListDao->children; eDao; eDao = eDao->next) { roomData_addRoomFromDao(zone, eDao); } } } /** * Load a room from it's entry dao. * * This loads it directly into the zone's rooms hashMap * * @param zone Zone room is being loaded into * @param roomDao daoData_t of the room to load * @return none */ void roomData_addRoomFromDao(zoneData_t *zone, daoData_t *roomDao) { if (zone == NULL) { log("roomData_addRoomFromDao(): invalid 'zone' zoneData_t."); } else if (roomDao == NULL) { log("roomData_addRoomFromDao(): invalid 'roomDao' daoData_t."); } else { /* Declare and init the new room var */ roomData_t *room = roomData_new(zone, dao_keyInt(roomDao, -1)); daoData_t *subDao = NULL; if (room != NULL) { /* Now let's set data on the room */ /* Name */ roomData_setName(room, (char *)dao_queryString(roomDao, "name", "This room needs a name!")); /* Description */ roomData_setDescription(room, (char *)dao_queryString(roomDao, "description", "This room needs a description!\n")); /* Sector Type */ room->sectorType = dao_queryType(roomDao, "sectorType", sector_types, 0); /* Room Flags */ room->roomFlags = dao_queryBits(roomDao, "flags", room_bits, 0); /* Exits */ subDao = dao_query(roomDao, "exits"); if (subDao != NULL) { roomExitData_loadRoomExits(room, subDao); } subDao = NULL; /* Extra Descriptions */ subDao = dao_query(roomDao, "extraDescriptions"); if (subDao != NULL) { room->exDescription = extraDescData_listFromDao(subDao); } subDao = NULL; /* Done! */ /* Our room is now loaded, and is in the hashMap for the zone */ } } } /** * Load room exits from dao * * @param room Room in which to set the exits * @param roomExitDao exit information to load, in dao format * @return none */ void roomExitData_loadRoomExits(roomData_t *room, daoData_t *roomExitDao) { daoData_t *eDao = NULL; if (roomExitDao != NULL) { for (eDao = roomExitDao->children; eDao; eDao = eDao->next) { /* Declare and init check vars based on DAO data */ register int dirNum = dao_keyType(eDao, dirs, -1); const char *eKeywords = dao_queryString(eDao, "keywords", NULL); const char *eDesc = dao_queryString(eDao, "description", NULL); const char *keyStr = dao_queryString(eDao, "key", NULL); const char *eToRoom = dao_queryString(eDao, "destinationRoom", NULL); /* If we didn't get a valid direction, error, and on to the next */ if (dirNum == -1) { log("roomExitData_loadRoomExits(): Invalid exit direction '%s' in room %d (%s)", eDao->key, room->number, room->name); continue; } /* Create the roomExitData for the exit */ CREATE(room->dir[dirNum], roomExitData_t, 1); /* First init values for sanity's sake */ room->dir[dirNum]->keyword = NULL; room->dir[dirNum]->generalDescription = NULL; room->dir[dirNum]->keyString = NULL; room->dir[dirNum]->key = NULL; room->dir[dirNum]->toRoomString = NULL; room->dir[dirNum]->toRoom = NULL; /* And populate */ if (eKeywords) room->dir[dirNum]->keyword = strdup(eKeywords); if (eDesc) room->dir[dirNum]->generalDescription = strdup(eDesc); if (eToRoom) room->dir[dirNum]->toRoomString = strdup(eToRoom); if (keyStr) room->dir[dirNum]->keyString = strdup(keyStr); room->dir[dirNum]->exitInfo = dao_queryBits(eDao, "flags", exit_bits, 0); } } } /* ************************************************************************ */ /* General room functions */ /* ************************************************************************ */ /** * Create a new room in passed zone * * If a room is already found in the zone specified then we'll re-use that * entry as opposed to creating a new one in a different location which * would break any pointers to the existing * * @param zone Zone to create the room in * @param rVnum VNum for room being created * @return pointer to roomData_t for the new room */ roomData_t *roomData_new(zoneData_t *zone, roomVnum_t rVnum) { roomData_t *room = NULL; if (zone == NULL) { log("roomData_new(): invalid 'zone' zoneData_t."); } else if (rVnum < 0) { log("roomData_new(): invalid 'rVnum' roomVnum_t."); } else { /* Declare iterator var */ register int dNum = 0; /* If the zone's rooms hashMap doesn't exist, create it */ if (zone->rooms == NULL) { zone->rooms = hashMap_create(0, hashVnum, NULL, roomData_free); if (zone->rooms == NULL) { log("roomData_new(): NULL returned from hashMap_create(). Aborting."); exit(1); } } /* Attempt to get a room with that VNum from the zone */ room = roomData_findInZone(zone, rVnum); /* If we don't have a room returned, create one. Otherwise free it out */ if (room == NULL) { CREATE(room, roomData_t, 1); /* Set the VNum */ room->number = rVnum; /* Since it wasn't in the hashMap, add it */ hashMap_insert(zone->rooms, &room->number, room); } else { if (room->name) free(room->name); if (room->description) free(room->description); if (room->exDescription) free_extra_descriptions(room->exDescription); for (dNum = 0; dNum < NUM_OF_DIRS; dNum++) { if (room->dir[dNum]) { roomExitData_free(room->dir[dNum]); } } } /* Sanity's sake... set appropriately */ room->name = NULL; room->description = NULL; room->exDescription = NULL; for (dNum = 0; dNum < NUM_OF_DIRS; dNum++) { room->dir[dNum] = NULL; } room->zone = zone; } return (room); } /** * Set a room's name * * @param room Room that's being changed * @param name New name for room * @return none */ void roomData_setName(roomData_t *room, char *name) { if (room == NULL) { log("roomData_setName(): invalid 'room' roomData_t."); } else if (name == NULL || *name == '\0') { log("roomData_setName(): invalid 'name' char."); } else { if (room->name) free(room->name); room->name = strdup(name); } } /** * Set a room's description * * @param room Room that's being changed * @param name New name for room * @return none */ void roomData_setDescription(roomData_t *room, char *description) { if (room == NULL) { log("roomData_setDescription(): invalid 'room' roomData_t."); } else if (description == NULL || *description == '\0') { log("roomData_setDescription(): invalid 'description' char."); } else { if (room->description) free(room->description); room->description = strdup(description); } } /** * Free a room entry * * This function takes a void pointer and casts back to a roomData_t so * that it will work with the hashMap code. * * @param r Room entry to be freed (void pointer) * @return none */ void roomData_free(void *r) { roomData_t *room = (roomData_t *)(r); if (room->name) free(room->name); if (room->description) free(room->description); if (room->contents) roomData_emptyContents(room); if (room->people) roomData_emptyPeople(room); if (room->exDescription) free_extra_descriptions(room->exDescription); free(room); room = NULL; } /** * Free a room exit entry * * @param roomExit Room exit entry to free * @return none */ void roomExitData_free(roomExitData_t *roomExit) { if (roomExit != NULL) { if (roomExit->keyword) free(roomExit->keyword); if (roomExit->generalDescription) free(roomExit->generalDescription); if (roomExit->toRoomString) free(roomExit->toRoomString); roomExit->toRoom = NULL; free(roomExit); roomExit = NULL; } } /** * Empty people from a room. * * Take all PCs in a room and remove them, sending them to the correct load * room based on mortal/immortal or freeze-room * * NPCs will be removed completely. * * @param room Room to be emptied of people * @return none * * @todo Actually code this */ void roomData_emptyPeople(roomData_t *room) { } /** * Empty contents from a room. * * Take all item contents from a room and remove them, sending them to either * a donation room if any exist, or extract them into oblivion. * * @param room Room to be emptied of items * @return none * * @todo Actually code this */ void roomData_emptyContents(roomData_t *room) { } /** * Find a room in hash map of rooms, by number * * (To be honest, I don't know if this will really be needed, but for now it's * just an abstraction used by the following function.) * * @param roomList hashMap_t of rooms * @param roomNumber Number (VNum) of room to find * @return pointer to room or NULL */ roomData_t *roomData_findByNumber(hashMap_t *roomList, int roomNumber) { return (roomData_t*) hashMap_getValue(roomList, &roomNumber, NULL); } /** * Find a room in a zone, by number * * @param zone Zone to check for the room * @param roomNumber Number (VNum) of room to find * @return pointer to room or NULL */ roomData_t *roomData_findInZone(zoneData_t *zone, int roomNumber) { return roomData_findByNumber(zone->rooms, roomNumber); } /** * Find a room in the world by segmented "vnum" * * This is the function which whill be used the most, as it gives a referece * to the zone by keyword and the room by number * * @param vnumString Segemented vnum string * @return pointer to the room or NULL */ roomData_t *roomData_find(char *vnumString) { roomData_t *room = NULL; if (vnumString == NULL || *vnumString == '\0') { log("roomData_find(): Invalid char 'vnumString'."); } else { zoneData_t *zone = NULL; char *z = NULL, *s = NULL; char *holder = strdup(vnumString); int rv = 0; z = holder; s = strstr(holder, ":"); if (s) { *s = '\0'; s++; rv = atoi(s); } if (z) { zone = zoneData_find(z); if (zone && zone->rooms) { room = roomData_findInZone(zone, rv); } } if (holder) free(holder); } return room; } /** * Find a room for a character. * * This function does some processing to let the user "drop" the zone if they want * to reference a room in the current zone. * * @param ch Character to look up a room for * @param vnumString vnum string which might be segmented or not */ roomData_t *roomData_findForChar(charData_t *ch, char *vnumString) { roomData_t *room = NULL; if (vnumString == NULL || *vnumString == '\0') { log("roomData_findForChar(): Invalid char 'vnumString'."); } else if (ch == NULL) { log("roomData_findForChar(): Invalid charData_t 'ch'."); } else { char segmented[MAX_INPUT_LENGTH] = { '\0' }; if (!strstr(vnumString, ":")) { snprintf(segmented, sizeof(segmented), "%s:%s", ch->room->zone->keyword, vnumString); room = roomData_find(segmented); } else { room = roomData_find(vnumString); } } return room; } /** * Add a single room to a buffer * * @param buf Buffer to list room in * @param room Room to list * @return none */ void room_addToList(char *buf, roomData_t *room) { if (room == NULL) { log("room_addToList(): Invalid roomData_t 'room'."); } else { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%15s:%-7d -- %s\r\n", room->zone->keyword, room->number, room->name); } } /** * List all rooms in a zone matching type into a buffer * * @param type Type of rooms to list * @param buf Buffer to generate list in * @param zone Zone who's rooms to list * @return none */ void rooms_listInZone(int type, char *buf, zoneData_t *zone) { if (zone == NULL) { log("rooms_listInZone(): Invalid zoneData_t 'zone'."); } else if (zone->rooms != NULL) { hashMapIterator_t *iter = hashMapIterator_create(zone->rooms); while (iter && hashMapIterator_getValue(iter)) { roomData_t *room = (roomData_t *)hashMapIterator_getValue(iter); if (room) { switch(type) { case DEATHTRAPS: if (ROOM_FLAGGED(room, ROOM_DEATH)) room_addToList(buf, room); break; case WIZROOMS: if (ROOM_FLAGGED(room, ROOM_WIZROOM)) room_addToList(buf, room); break; } } room = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } }