circlemud_squared_0.5.153/cnf/
circlemud_squared_0.5.153/etc/
circlemud_squared_0.5.153/etc/etc/
circlemud_squared_0.5.153/etc/house/
circlemud_squared_0.5.153/etc/misc/
circlemud_squared_0.5.153/etc/plralias/A-E/
circlemud_squared_0.5.153/etc/plralias/F-J/
circlemud_squared_0.5.153/etc/plralias/K-O/
circlemud_squared_0.5.153/etc/plralias/P-T/
circlemud_squared_0.5.153/etc/plralias/U-Z/
circlemud_squared_0.5.153/etc/plralias/ZZZ/
circlemud_squared_0.5.153/etc/plrobjs/
circlemud_squared_0.5.153/etc/plrobjs/A-E/
circlemud_squared_0.5.153/etc/plrobjs/F-J/
circlemud_squared_0.5.153/etc/plrobjs/K-O/
circlemud_squared_0.5.153/etc/plrobjs/P-T/
circlemud_squared_0.5.153/etc/plrobjs/U-Z/
circlemud_squared_0.5.153/etc/plrobjs/ZZZ/
circlemud_squared_0.5.153/etc/text/
circlemud_squared_0.5.153/etc/text/help/
circlemud_squared_0.5.153/src/util/
circlemud_squared_0.5.153/src/util/worldconv/
/**
 * @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);
  }
}