/* -*- LPC -*- */ /* * $Locker: pinkfish $ * $Id: topography.c,v 1.9 2002/02/28 23:13:52 pinkfish Exp pinkfish $ * * */ /* * $Locker: pinkfish $ * $Id: topography.c,v 1.9 2002/02/28 23:13:52 pinkfish Exp pinkfish $ * * */ /* ** topography.c - the top-level handler that pulls together the areas, ** irooms, and fixed rooms. */ /** * This is the top-level handler for the topography system. */ #include <dirs.h> #include "path.h" //inherit "/std/room"; inherit "/std/basic/setup"; class bounding_box { int* topleft; int* bottomright; } class FIXED_INFO { string path; object ob; int *coord; mixed bbox; } private mapping opposites = ([ "north": "south", "south": "north", "west": "east", "east": "west", "northeast": "southwest", "southwest": "northeast", "northwest": "southeast", "southeast": "northwest", "up": "down", "down": "up" ]); private int _dbg_lvl = 0; private string _debugger; private object *_area_handlers = ({ }); private object *_iroom_handlers = ({ }); private class FIXED_INFO *_fixed_rooms = ({ }); private mapping _fixed_room_index = ([ ]); private class bounding_box _topo_box; /** * This method sets the creator to whom debugging messages are sent. * @param d name of creator */ void set_debugger(string s) { _debugger = s; } /** * This method returns the name of the creator to whom debugging messages * are sent. * @return name of creator */ string query_debugger() { return _debugger; } /** * This method sets the level of debugging messages printed. * @param l debugging level (0 is off) */ void set_debug_level(int l) { _dbg_lvl = l; } /** * This method returns the current debugging level. * @return current debug level (0 is off) */ int query_debug_level() { return _dbg_lvl; } /** * @ignore * For debugging only */ //mixed query(string s) { return fetch_variable(s); } void create() { //inc_setup(); //::create(); //dec_setup(); do_setup(); } /* create() */ /** * This method returns the bounding box for the topography. * @return the bounding box */ class bounding_box query_bounding_box() { return _topo_box; } /* query_bounding_box() */ /** * Checks to see if the co-ordinate is in the bounding box. * @param coord the co-ordinate to check * @param bbox the bounding box * @return 1 if it is, 0 if not */ int in_bbox(int *coord, class bounding_box bbox) { if ((coord[0] < bbox->topleft[0]) || (coord[0] > bbox->bottomright[0])) { return 0; } if ((coord[1] < bbox->topleft[1]) || (coord[1] > bbox->bottomright[1])) { return 0; } return 1; } /* in_bbox() */ /** * This method makes a bounding box for a specified co ordinate and * a polygon. * @param coord the co-ordinate to get the box from * @param s the size (or integer square size) of the room * @return the bounding box */ class bounding_box bounding_box(int *coord, mixed s) { int *size; class bounding_box bbox; if (sizeof(coord) != 3) { return 0; } if (arrayp(s)) { if (sizeof(s) == 3) { size = s; } else { return 0; } } else if (intp(s)) { size = ({ s, s, s }); } else { return 0; } bbox = new(class bounding_box); bbox->topleft = ({ coord[0]-size[0], coord[1]-size[1], coord[2]-size[2] }); bbox->bottomright = ({ coord[0]+size[0], coord[1]+size[1], coord[2]+size[2] }); return bbox; } /* bounding_box() */ /** * This method adds in an area handler. * @param h the area handle to add */ void add_area_handler(mixed h) { object oh; if (stringp(h)) { oh = load_object(h); oh->set_handler(base_name(this_object())); if (objectp(oh)) { _area_handlers += ({ oh }); } else { error(sprintf("Couldn't find area handler %s.\n", h)); } } else if (objectp(h)) { h->set_handler(base_name(this_object())); _area_handlers += ({ h }); } else { error(sprintf("Couldn't find area handler %O.\n", h)); } } /* add_area_handler() */ /** * This method adds in an inter-room handler. An interroom is something like * a path that has a rectangular bounding box allowing multiple rooms to * connect to it. * @param h the interoom handler */ void add_iroom_handler(mixed h) { object oh; if (stringp(h)) { oh = load_object(h); oh->set_handler(base_name(this_object())); if (objectp(oh)) { _iroom_handlers += ({ oh }); } else { error(sprintf("Couldn't find interroom handler %s.\n", h)); } } else if (objectp(h)) { h->set_handler(base_name(this_object())); _iroom_handlers += ({ h }); } else { error(sprintf("Couldn't find interroom handler %O.\n", h)); } } /* add_iroom_handler() */ /** * This is the internal function to add a fixed location room. * @param path the path to the object * @param ob the object itself * @param coord the coordinate of the object * @param bbox the bounding box of the object */ private void add_internal_fixed_room(string path, object ob, int* coord, class bounding_box bbox) { class FIXED_INFO cf; cf = new(class FIXED_INFO); cf->path = path; cf->ob = ob; cf->coord = coord; cf->bbox = bbox; _fixed_room_index[cf->path] = sizeof(_fixed_rooms); _fixed_rooms += ({ cf }); } /* add_internal_fixed_room() */ /** * This method adds a room in a fixed location. It gets the co-ordinate * and size information from the room itself. * @param f the room to add * @add add_fixed_room_object() * @add add_fixed_room_coord() */ void add_fixed_room(string f) { object of; of = load_object(f); if (!objectp(of)) { error(sprintf("Couldn't load fixed room %s.\n", f)); return; } add_internal_fixed_room(f, of, of->query_co_ord(), bounding_box(of->query_co_ord(), of->query_room_size())); } /* add_fixed_room() */ /** * This method adds a fixed room from an object. * @param of the object file * @add add_fixed_room() * @add add_fixed_room_coord() */ void add_fixed_room_object(object of) { add_internal_fixed_room(file_name(of), of, of->query_co_ord(), bounding_box(of->query_co_ord(), of->query_room_size())); } /* add_fixed_room() */ /** * This method adds a fixed room with a room and a co-ordinate. * @param f the file name of the room * @param coord the coordinate of the room * @param size the size of the room * @add add_fixed_room_object() * @add add_fixed_room() */ void add_fixed_room_coordinate(string f, int* coord, int* size) { add_internal_fixed_room(f, 0, coord, bounding_box(coord, size)); } /* add_fixed_room_coordinate() */ /** * This method is called from a loaded object to setup the fixed room * into the topology. */ void setup_fixed_room() { object shad, r; int i; r = previous_object(); shad = clone_object(FIXED_SHADOW); if (!objectp(shad)) { error(sprintf("Couldn't create shadow for fixed room %O.\n", r)); return; } shad->attach(r); shad->set_area_handler(file_name(this_object())); if (undefinedp(i = _fixed_room_index[file_name(r)])) { error(sprintf("Couldn't find entry for fixed room %O.\n", r)); } r->set_co_ord(_fixed_rooms[i]->coord); //tell_creator("jeremy", "%O coords = %O\n", r, r->query_co_ord()); } /* setup_fixed_room() */ /** * This method finds the room as the specified co-ordinates. * @param coord the coordinate to look up the room at * @return the room */ object find_room_at_coord(int *coord) { int i; object ob; if (_dbg_lvl > 0) { tell_creator(_debugger, "%s::find_room_at_coord()\n" " coord: %O\n", file_name(this_object()), coord); } // Check fixed rooms first... for (i = 0; i < sizeof(_fixed_rooms); i++) { if (in_bbox(coord, _fixed_rooms[i]->bbox)) { if (!objectp(_fixed_rooms[i]->ob)) { _fixed_rooms[i]->ob = load_object(_fixed_rooms[i]->path); } return _fixed_rooms[i]->ob; } } // ...then irooms... for (i = 0; i < sizeof(_iroom_handlers); i++) { if (objectp(ob = _iroom_handlers[i]->find_room_at_coord(coord))) { return ob; } } // ...finally, check areas... for (i = 0; i < sizeof(_area_handlers); i++) { if (objectp(ob = _area_handlers[i]->find_room_at_coord(coord))) { return ob; } } return 0; } /* find_room_at_coord() */ /** * This method does a virtual object lookup for the room. * @param x the x coord * @param y the y coord * @param z the z coord */ object create_virtual_object(int x, int y, int z) { if (undefinedp(x) || undefinedp(y) || undefinedp(z)) { return 0; } return find_room_at_coord(({ x, y, z })); } /* create_virtual_object() */ /** * This method finds the room at the specified exit in the specified room. * @param r the room to look up * @param d the dimension of the room * @param direc the direction we are moving in * @return the room at the exit */ object find_room_at_exit(object r, int *d, string direc) { int i, s, dist, max_dist, max_iroom, *coord, *coord_r, *coord_ob; string msg; object ob, *irooms = ({ }); mixed size_r; class bounding_box bbox_r; if (_dbg_lvl > 0) { tell_creator(_debugger, "%s::find_room_at_exit()\n" " r: %O\n" " d: %O\n" " direc: %O\n", file_name(this_object()), r, d, direc); } if (!arrayp(d)) { return 0; } coord = r->query_co_ord(); // Some rooms (like milestones) have "fake" sizes. size_r = r->query_actual_room_size(); if (!arrayp(size_r)) { size_r = r->query_room_size(); if (!arrayp(size_r)) { // Must be a single int for all 3 dimensions. size_r = ({ size_r, size_r, size_r }); } } coord_r = allocate(3); for (i = 0; i < 3; i++) { // This assumes the rooms are adjacent, either on an edge or at // a corner (note to self: figure out why this seems to hit areas // before irooms). //coord_r[i] = coord[i] + (size_r[i] + 1) * d[i]; coord_r[i] = coord[i] + 2 * size_r[i] * d[i]; } // Check fixed rooms first... for (i = 0; i < sizeof(_fixed_rooms); i++) { if (in_bbox(coord_r, _fixed_rooms[i]->bbox)) { if (!objectp(_fixed_rooms[i]->ob)) { _fixed_rooms[i]->ob = load_object(_fixed_rooms[i]->path); } // Check that the fixed room allows entrances from this direction. msg = evaluate(_fixed_rooms[i]->ob->query_topo_barrier(direc)); if (!undefinedp(msg)) { if (!stringp(msg)) { msg = "You can't go that way.\n"; } notify_fail(msg); return 0; } return _fixed_rooms[i]->ob; } } // ...then irooms... // This is a bit complicated. I need to check the distance to // the rooms returned by all the handlers and pick the closest. bbox_r = bounding_box(coord, size_r); for (i = 0; i < sizeof(_iroom_handlers); i++) { ob = _iroom_handlers[i]->find_room_at_crossing(coord, coord_r); tell_creator("rhinehold", "ob = %O\n", ob); if (objectp(ob /*(= _iroom_handlers[i]->find_room_at_crossing(coord, coord_r)*/) && (ob != r)) { if (_dbg_lvl > 1) { tell_creator(_debugger, "%s::find_room_at_exit()\n" " %O->find_room_at_crossing() returned %O\n", file_name(this_object()), _iroom_handlers[i], ob); } // This is a kludge, but I can't think of a way around it. Milestones // (like all normal rooms), get their coords set in a callout, so // I can't calculate the distance here. I'll have to assume that // a room without coordinates is the "correct" one. if (!arrayp(ob->query_co_ord())) { return ob; } // Ignore irooms that are in the current room's bounding box, if // the current room is a fixed room. // *** NOTE: I'm not sure if this is sufficient. There are cases // where I want irooms to take precedence (like the path around // the maze), but there are others where the current room should // take precedence (like s_cara03b). Hopefully, the latter case // is always a fixed room. if (r->query_fixed_room() && in_bbox(ob->query_co_ord(), bbox_r)) { continue; } irooms += ({ ob }); } } if ((s = sizeof(irooms)) > 0) { if (s == 1) { return irooms[0]; } else { // Hopefully this won't happen often... max_dist = 0; max_iroom = -1; for (i = 0; i < s; i++) { coord_ob = irooms[i]->query_co_ord(); dist = (coord[0] - coord_ob[0])*(coord[0] - coord_ob[0]) + (coord[1] - coord_ob[1])*(coord[1] - coord_ob[1]); if (dist > max_dist) { max_dist = dist; max_iroom = i; } } if (max_iroom >= 0) { return irooms[max_iroom]; } } } // ...finally, check areas... for (i = 0; i < sizeof(_area_handlers); i++) { if (objectp(ob = _area_handlers[i]->find_room_at_exit(r, d))) { if (_dbg_lvl > 1) { tell_creator(_debugger, "%s::find_room_at_exit()\n" " %O->find_room_at_exit() returned %O\n", file_name(this_object()), _area_handlers[i], ob); } return ob; } } return 0; } /* find_room_at_exit() */ // This is a testing convenience function /** * This method allows you to be moved quickly to a room at the specified * co-ordinate. * @param coord the coordinate to go to */ int goto_room_at_coord(int *coord) { return this_player()->move_with_look(find_room_at_coord(coord)); } /* goto_room_at_coord() */ /** * This method will make a map of the specified width based on the data in * the topography area. * @param width the width of the map * @return the map */ string query_text_map(int width) { int x; int y; int maxy; string map; object room; map = ""; for (y = 0; y < maxy; y++) { for (x = 0; x < width; x++) { room = find_room_at_coord( ({ x, y, 0 }) ); if (!room) { map += " "; } else if (!room->query_room_letter()) { map += "?"; } else { map += room->query_room_letter(); } } map += "\n"; } return map; } /* query_text_map() */