//***************************************************************************** // // 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; }