nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// room.c
//
// the basic implementation of the room data structure. If you plan on adding
// any other information to rooms, it is strongly suggested you do so through
// auxiliary data (see auxiliary.h)
//
// For a recap, IF YOU PLAN ON ADDING ANY OTHER INFORMATION TO ROOMS, IT
// IS STRONGLY SUGGESTED YOU DO SO THROUGH AUXILIARY DATA (see auxiliary.h).
//
//*****************************************************************************

#include "mud.h"
#include "utils.h"
#include "handler.h"
#include "extra_descs.h"
#include "auxiliary.h"
#include "storage.h"
#include "exit.h"
#include "room.h"



// room UIDs (unique IDs) start at a million and go 
// up by one every time a new room is created
#define START_ROOM_UID       1000000
int next_room_uid  =   START_ROOM_UID;

struct room_data {
  int         uid;               // what is our unique room ID number?
  int         terrain;           // what kind of terrain do we have?
  char       *name;              // what is the name of our room?
  BUFFER     *desc;              // our description

  HASHTABLE  *exits;             // a dir:exit mapping
  NEAR_MAP   *cmd_table;         // a listing for all our room-only commands
  EDESC_SET  *edescs;            // the extra descriptions in the room
  BITVECTOR  *bits;              // the bits we have turned on
  char       *class;             // what prototype do we directly inherit?
  char       *prototypes;        // what prototypes are we instances of?

  LIST       *contents;          // what objects do we contain in the room?
  LIST       *characters;        // who is in our room?

  HASHTABLE  *auxiliary_data;    // data modules have installed in us
};


//*****************************************************************************
//
// implementation of the room.h interface
//
//*****************************************************************************
ROOM_DATA *newRoom() {
  ROOM_DATA *room = malloc(sizeof(ROOM_DATA));

  room->uid       = next_room_uid++;
  room->prototypes= strdup("");
  room->name      = strdup("");
  room->class     = strdup("");
  room->desc      = newBuffer(1);

  room->terrain = TERRAIN_INDOORS;

  room->bits           = bitvectorInstanceOf("room_bits");
  room->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_ROOM);

  room->exits      = newHashtable();
  room->cmd_table  = newNearMap();
  room->edescs     = newEdescSet();
  room->contents   = newList();
  room->characters = newList();

  return room;
}


void deleteRoom(ROOM_DATA *room) {
  LIST_ITERATOR *cont_i = NULL;
  void         *content = NULL;

  // Extract all of our contents. Afterwards, delete the lists.
  cont_i = newListIterator(room->contents);
  ITERATE_LIST(content, cont_i)
    extract_obj(content);
  deleteListIterator(cont_i);

  cont_i = newListIterator(room->characters);
  ITERATE_LIST(content, cont_i)
    extract_mobile(content);
  deleteListIterator(cont_i);
  
  // delete contents
  // oops! This ain't cool... we can't delete these things with calls to
  // extract() because those extract remove the objs/chars from the room. If
  // we try removing when we're in the process of deleting lists, bad things
  // happen. So now we extract first, and THEN delete the room lists.
  //  deleteListWith(room->contents,   extract_obj);
  //  deleteListWith(room->characters, extract_mobile);
  deleteList(room->contents);
  deleteList(room->characters);

  // delete all of our exits
  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
  const char     *dir = NULL;
  EXIT_DATA       *ex = NULL;
  ITERATE_HASH(dir, ex, ex_i)
    deleteExit(ex);
  deleteHashIterator(ex_i);
  deleteHashtable(room->exits);

  // delete all of our commands
  NEAR_ITERATOR *cmd_i = newNearIterator(room->cmd_table);
  const char      *key = NULL;
  CMD_DATA        *cmd = NULL;
  ITERATE_NEARMAP(key, cmd, cmd_i)
    deleteCmd(cmd);
  deleteNearIterator(cmd_i);
  deleteNearMap(room->cmd_table);

  // delete extra descriptions
  if(room->edescs) deleteEdescSet(room->edescs);

  // delete bits
  if(room->bits) deleteBitvector(room->bits);

  // delete strings
  if(room->prototypes) free(room->prototypes);
  if(room->class)      free(room->class);
  if(room->name)       free(room->name);
  if(room->desc)       deleteBuffer(room->desc);
  deleteAuxiliaryData(room->auxiliary_data);

  free(room);
}


STORAGE_SET *roomStore(ROOM_DATA *room) {
  STORAGE_SET          *set = new_storage_set();
  STORAGE_SET_LIST *ex_list = new_storage_list();
  store_string(set, "class",      room->class);
  store_string(set, "prototypes", room->prototypes);
  store_string(set, "name",       room->name);
  store_string(set, "desc",       bufferString(room->desc));
  store_string(set, "terrain",    terrainGetName(room->terrain));
  store_set   (set, "edescs",     edescSetStore(room->edescs));
  store_list  (set, "exits",      ex_list);

  // store all of our exits. We're doing this in an odd way by putting the
  // direction name on the storage set for the exit. They should probably be
  // in different storage sets, and nested in another key:val pair storage set.
  // But this is the way we started doing it, and for the sake of compatibility,
  // we're going to keep at it...
  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
  const char     *dir = NULL;
  EXIT_DATA       *ex = NULL;
  ITERATE_HASH(dir, ex, ex_i) {
    STORAGE_SET *ex_set = exitStore(ex);
    store_string(ex_set, "direction", dir);
    storage_list_put(ex_list, ex_set);
  } deleteHashIterator(ex_i);

  store_set   (set, "auxiliary", auxiliaryDataStore(room->auxiliary_data));
  return set;
}


ROOM_DATA *roomRead(STORAGE_SET *set) {
  ROOM_DATA           *room = newRoom();
  STORAGE_SET_LIST *ex_list = read_list(set, "exits");
  STORAGE_SET       *ex_set = NULL;
  roomSetClass(room, read_string(set, "class"));
  roomSetPrototypes(room, read_string(set, "prototypes"));
  roomSetName(room,       read_string(set, "name"));
  roomSetDesc(room,       read_string(set, "desc"));
  roomSetTerrain(room,    terrainGetNum(read_string(set,"terrain")));
  roomSetEdescs(room,     edescSetRead(read_set   (set, "edescs")));
  bitSet(room->bits,      read_string(set, "room_bits"));

  // parse and add all of our exits
  while( (ex_set = storage_list_next(ex_list)) != NULL)
    roomSetExit(room, read_string(ex_set, "direction"), exitRead(ex_set));

  deleteAuxiliaryData(room->auxiliary_data);
  room->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"), 
					   AUXILIARY_TYPE_ROOM);
  return room;
}



ROOM_DATA *roomCopy(ROOM_DATA *room) {
  ROOM_DATA *R = newRoom();
  roomCopyTo(room, R);
  return R;
}


void roomCopyTo(ROOM_DATA *from, ROOM_DATA *to) {
  // we just want to copy data ABOUT the room, and not stuff
  // contained in the particular instance (e.g. players, contents)
  roomSetClass     (to, roomGetClass(from));
  roomSetPrototypes(to, roomGetPrototypes(from));
  roomSetClass     (to, roomGetClass(from));
  roomSetName      (to, roomGetName(from));
  roomSetDesc      (to, roomGetDesc(from));
  roomSetTerrain   (to, roomGetTerrain(from));
  bitvectorCopyTo  (from->bits, to->bits);

  // set our edescs
  roomSetEdescs(to, edescSetCopy(from->edescs));

  // copy all of our exits. Augh, this is ugly. If we're copying from a room to
  // another room that is in game, we have to put all of the new exits in game
  // as well, and remove all of the old exits from game. Its sort of hackish to
  // do it here since the room datastructure should have no concept of in/out
  // of game, but there's really nowhere else to put this...
  bool does_room_exist = room_exists(to);
  bool room_in_game    = listIn(room_list, to);

  // first, delete all of our old exits
  HASH_ITERATOR *ex_i = newHashIterator(to->exits);
  const char     *dir = NULL;
  EXIT_DATA       *ex = NULL;
  ITERATE_HASH(dir, ex, ex_i) {
    hashRemove(to->exits, dir);
    if(does_room_exist) exit_from_game(ex);
    deleteExit(ex);
  } deleteHashIterator(ex_i);

  // now, copy all of our new exits
  ex_i = newHashIterator(from->exits);
  ITERATE_HASH(dir, ex, ex_i) {
    roomSetExit(to, dir, exitCopy(ex));
    if(does_room_exist) exit_exist(ex);
    if(room_in_game)    exit_to_game(roomGetExit(to, dir));
  } deleteHashIterator(ex_i);

  // delete all of our old commands
  NEAR_ITERATOR *cmd_i = newNearIterator(to->cmd_table);
  const char   *abbrev = NULL;
  CMD_DATA        *cmd = NULL;
  ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
    nearMapRemove(to->cmd_table, cmdGetName(cmd));
    deleteCmd(cmd);
  } deleteNearIterator(cmd_i);

  // now, copy in all of our new commands
   cmd_i = newNearIterator(from->cmd_table);
   ITERATE_NEARMAP(abbrev, cmd, cmd_i) {
     nearMapPut(to->cmd_table, cmdGetName(cmd), abbrev, cmdCopy(cmd));
   } deleteNearIterator(cmd_i);
  
  // copy all of our auxiliary data
  auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
}

bool roomIsInstance(ROOM_DATA *room, const char *prototype) {
  return is_keyword(room->prototypes, prototype, FALSE);
}

const char *roomGetPrototypes(ROOM_DATA *room) {
  return room->prototypes;
}

void roomAddPrototype(ROOM_DATA *room, const char *prototype) {
  add_keyword(&room->prototypes, prototype);
}

void roomSetPrototypes(ROOM_DATA *room, const char *prototypes) {
  if(room->prototypes) free(room->prototypes);
  room->prototypes = strdupsafe(prototypes);
}



//*****************************************************************************
// add and remove functions
//*****************************************************************************
void roomRemoveChar(ROOM_DATA *room, const CHAR_DATA *ch) {
  listRemove(room->characters, ch);
}

void roomRemoveObj(ROOM_DATA *room, const OBJ_DATA *obj) {
  listRemove(room->contents, obj);
}

void roomAddChar(ROOM_DATA *room, CHAR_DATA *ch) {
  listPut(room->characters, ch);
}

void roomAddObj(ROOM_DATA *room, OBJ_DATA *obj) {
  listPut(room->contents, obj);
}



//*****************************************************************************
// exit functions
//*****************************************************************************
void roomSetExit(ROOM_DATA *room, const char *dir, EXIT_DATA *exit) {
  hashPut(room->exits, dir, exit);
  exitSetRoom(exit, room);
}

EXIT_DATA *roomGetExit(ROOM_DATA *room, const char *dir) {
  return hashGet(room->exits, dir);
}

EXIT_DATA *roomRemoveExit(ROOM_DATA *room, const char *dir) {
  EXIT_DATA *exit = hashRemove(room->exits, dir);
  if(exit != NULL) exitSetRoom(exit, NULL);
  return exit;
}

const char *roomGetExitDir(ROOM_DATA *room, EXIT_DATA *exit) {
  // go through all of our key:val pairs, and see which key matches this exit
  HASH_ITERATOR *ex_i = newHashIterator(room->exits);
  const char     *dir = NULL;
  EXIT_DATA       *ex = NULL;
  ITERATE_HASH(dir, ex, ex_i) {
    if(ex == exit) {
      deleteHashIterator(ex_i);
      return dir;
    }
  } deleteHashIterator(ex_i);
  return NULL;
}

LIST *roomGetExitNames(ROOM_DATA *room) {
  return hashCollect(room->exits);
}



//*****************************************************************************
// get and set functions for rooms
//*****************************************************************************
const char *roomGetClass(ROOM_DATA *room) {
  return room->class;
}

void roomSetClass(ROOM_DATA *room, const char *prototype) {
  if(room->class) free(room->class);
  room->class = strdupsafe(prototype);
}

LIST       *roomGetContents    (const ROOM_DATA *room) {
  return room->contents;
}

LIST       *roomGetCharacters  (const ROOM_DATA *room) {
  return room->characters;
}

const char *roomGetName        (const ROOM_DATA *room) {
  return room->name;
}

const char *roomGetDesc        (const ROOM_DATA *room) {
  return bufferString(room->desc);
}

BUFFER *roomGetDescBuffer(const ROOM_DATA *room) {
  return room->desc;
}

int         roomGetTerrain     (const ROOM_DATA *room) {
  return room->terrain;
}

int         roomGetUID         (const ROOM_DATA *room) {
  return room->uid;
}

EDESC_SET  *roomGetEdescs      (const ROOM_DATA *room) {
  return room->edescs;
}

const char *roomGetEdesc       (const ROOM_DATA *room, const char *keyword) {
  EDESC_DATA *edesc = edescSetGet(room->edescs, keyword);
  if(edesc) return edescSetGetDesc(edesc);
  else return NULL;
}

void *roomGetAuxiliaryData     (const ROOM_DATA *room, const char *name) {
  return hashGet(room->auxiliary_data, name);
}

void        roomSetEdescs      (ROOM_DATA *room, EDESC_SET *edescs) {
  if(room->edescs) deleteEdescSet(room->edescs);
  room->edescs = edescs;
}

void        roomSetName        (ROOM_DATA *room, const char *name) {
  if(room->name) free(room->name);
  room->name = strdupsafe(name);
}

void        roomSetDesc (ROOM_DATA *room, const char *desc) {
  bufferClear(room->desc);
  bufferCat(room->desc, (desc ? desc : ""));
}

void        roomSetTerrain     (ROOM_DATA *room, int terrain_type) {
  room->terrain = terrain_type;
}

BITVECTOR *roomGetBits(const ROOM_DATA *room) {
  return room->bits;
}

NEAR_MAP *roomGetCmdTable(const ROOM_DATA *room) {
  return room->cmd_table;
}



//*****************************************************************************
// direction stuff
//*****************************************************************************
const char *dir_names[NUM_DIRS] = {
  "north",
  "east",
  "south",
  "west",
  "up",
  "down",
  "northeast",
  "southeast",
  "southwest",
  "northwest"
};

const char *dir_abbrevs[NUM_DIRS] = {
  "n", "e", "s", "w", "u", "d",
  "ne", "se", "sw", "nw"
};

const char *dirGetName(int dir) {
  return dir_names[dir];
}

const char *dirGetAbbrev(int dir) {
  return dir_abbrevs[dir];
}

int dirGetOpposite(int dir) {
  switch(dir) {
  case DIR_NORTH:        return DIR_SOUTH;
  case DIR_EAST:         return DIR_WEST;
  case DIR_SOUTH:        return DIR_NORTH;
  case DIR_WEST:         return DIR_EAST;
  case DIR_UP:           return DIR_DOWN;
  case DIR_DOWN:         return DIR_UP;
  case DIR_NORTHEAST:    return DIR_SOUTHWEST;
  case DIR_SOUTHEAST:    return DIR_NORTHWEST;
  case DIR_SOUTHWEST:    return DIR_NORTHEAST;
  case DIR_NORTHWEST:    return DIR_SOUTHEAST;
  default:               return DIR_NONE;
  }
}

int dirGetNum(const char *dir) {
  int i;
  for(i = 0; i < NUM_DIRS; i++)
    if(!strcasecmp(dir, dir_names[i]))
      return i;
  return DIR_NONE;
}

int dirGetAbbrevNum(const char *dir) {
  int i;
  for(i = 0; i < NUM_DIRS; i++)
    if(!strcasecmp(dir, dir_abbrevs[i]))
      return i;
  return DIR_NONE;
}


//*****************************************************************************
//
// terrain stuff
//
//*****************************************************************************
struct terrain_data {
  char   *name;
};

const struct terrain_data terrain_types[NUM_TERRAINS] = {
  { "Inside"        },
  { "City"          },
  { "Road"          },
  { "Alley"         },
  { "Bridge"        },
  { "Shallow Water" },
  { "Deep Water"    },
  { "Ocean"         },
  { "Underwater"    },
  { "Field"         },
  { "Plains"        },
  { "Meadow"        },
  { "Forest"        },
  { "Deep Forest"   },
  { "Hills"         },
  { "High Hills"    },
  { "Mountains"     },
  { "Swamp"         },
  { "Deep Swamp"    },
  { "Sand"          },
  { "Desert"        }, 
  { "Ice"           },
  { "Glacier"       },
  { "Cavern"        },
};


const char *terrainGetName(int terrain) {
  return terrain_types[terrain].name;
}

int terrainGetNum(const char *terrain) {
  int i;
  for(i = 0; i < NUM_TERRAINS; i++)
    if(!strcasecmp(terrain, terrain_types[i].name))
      return i;
  return TERRAIN_NONE;
}