/* -*- LPC -*- */ /* * $Locker: $ * $Id: handler_base.c,v 1.116 2003/07/14 19:44:09 pinkfish Exp $ * */ /** * @main * Base inherit for terrain map handler. * <p> * When creating a new terrain map, inherit from this file to make your area * handler. * <p> * When inheriting from this file, it is essential that the functions * query_map_file() and setup_handler() be overriden to return the full * pathname of the area map, and to set up type mappings. * <p> * To inherit from this file, use the following lines: * <ul> * <li>#include <terrain_map.h> * <li> * <li>inherit TERRAIN_MAP_HANDLER_BASE * </ul> * * @index terrain_map_handler * @see terrain_map_inside * @see terrain_map_outside * @see terrain_map_intro * @see terrain_map_example * @author Dek * @started Thurs April 12 2001 Too Damn Early BST **/ #include <weather.h> #include <terrain_map.h> #include <player.h> #define AREA_OBSTACLE 0 #define AREA_ROOM 1 #define AREA_ROAD 2 #define AREA_WORLD 3 #define RANDOM_HANDLER "/obj/handlers/random_num" inherit "/std/room/inherit/terrain_map/distance_str"; #define X_DIFF 1 #define Y_DIFF 0 #define XY_DIFF 2 class char_map_data { string char; string colour; string path; string room_desc; mixed items; mixed item_desc; mixed chats; int type; int height; mapping height_rooms; int whole_sentance; string adjacent_desc; // How many rooms to jump with each movement. int room_jump; // If this is 1 then we treat is as a road and try to follow twists. // If it is 0 then we go in the direction as far as we can. int room_jump_matching; } class feature_data { int range; string* descs; string* night_descs; mixed f_items; mixed f_item_desc; mixed f_item_night_desc; int whole_sentance; mixed chats; mixed night_chats; object feature_ob; } class sign_data { string long; string read_mess; string short; string name; string language; } class real_coords { int x1; int y1; int x2; int y2; int xdiff; int ydiff; int xydiff; // Diff in a diagonal. int zdiff; int in_world_map; } private mixed *_room_map = 0; private mixed *_area_map = 0; private mixed *_zone_map = 0; private int _visible_distance; private int _width = 0; private int _height = 0; private int _normal_skip_size = 1; private int _random_desc_limit = 2; private int _random_chat_limit = 4; private int _newline_mode = 1; private mapping _room_zones = ([ ]); private mapping _feature_rooms = ([ ]); private mapping _char_map = ([ ]); private mapping _zone_types = ([ ]); private mapping _features = ([ ]); private mapping _signposts = ([ ]); private mapping _standard_exits = ([ ]); private mapping _adjacent_terrains = ([ ]); private mapping _random_descs = ([ ]); private mapping _random_chats = ([ ]); private class real_coords _real_coords; private mapping _key = ([ ]); private mapping _direcs = ([ "north": ({ 0, 1, "south", Y_DIFF, 0 }), "northeast": ({ 1, 1, "southwest", XY_DIFF, ({ "obvious", 0 }) }), "east": ({ 1, 0, "west", X_DIFF, 0 }), "southeast": ({ 1, -1, "northwest", XY_DIFF, ({ "obvious", 0 }) }), "south": ({ 0, -1, "north", Y_DIFF, 0 }), "southwest": ({ -1, -1, "northeast", XY_DIFF, ({ "obvious", 0 }) }), "west": ({ -1, 0, "east", X_DIFF, 0 }), "northwest": ({ -1, 1, "southeast", XY_DIFF, ({ "obvious", 0 }) }) ]); // This is the order to do the directions in. Make sure we do the // main cardinal ones first. private string* _direc_order = ({ "north", "east", "south", "west", "northeast", "southeast", "southwest", "northwest" }); private mixed *_feature_direcs = ({ ({ "southwest", "south", "southeast" }), ({ "west", "Oops", "east" }), ({ "northwest", "north", "northeast" }) }); void load_area_map(); void load_zone_map(); class char_map_data query_char_map(int x, int y); void setup_area_map(); void setup_room(object room, string base_path); string find_adjacent_terrain(int x, int y); // A load of accessor functions to make debugging a lot easier. mixed *query_room_map() { return _room_map; } mixed *query_area_map() { return _area_map; } mixed *query_zone_map() { return _zone_map; } mapping query_zone_types() { return _zone_types; } mapping query_feature_rooms() { return _feature_rooms; } mapping query_features() { return _features; } mapping query_room_zones() { return _room_zones; } mapping query_char_maps() { return _char_map; } mapping query_signposts() { return _signposts; } mapping query_random_descs() { return _random_descs; } mapping query_random_chats() { return _random_chats; } mapping query_standard_exits() { return _standard_exits; } mapping query_adjacent_terrains() { return _adjacent_terrains; } class real_coords query_debug_real_coord() { return _real_coords; } mixed* query_real_coords() { return ({ ({ _real_coords->x1, _real_coords->y1 }), ({ _real_coords->x2, _real_coords->y2 }) }); } int *query_dimensions() { return ({ _width, _height }); } void setup_handler() { } string query_map_file() { return ""; } // OVERRIDE to return the map file path. // No, this makes us have a 700M driver and end up swapping like crazy! //int clean_up() { return 0; } /** * This method sets the real coordinates for the room without informing * the map handler. * @param x1 the start x location * @param y1 the start y location * @param x1 the end x location * @param y1 the end y location */ void set_real_coordinates_no_inform(int x1, int y1, int x2, int y2) { int tmp; if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if (y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } _real_coords = new(class real_coords, x1:x1, y1:y1, x2:x2, y2:y2); _real_coords->xdiff = (x2 - x1) / _width; _real_coords->ydiff = (y2 - y1) / _height; // All z differentials in 15 foot lengths _real_coords->zdiff = 15; _real_coords->xydiff = to_int(sqrt(to_float(_real_coords->xdiff) * to_float(_real_coords->xdiff)+ to_float(_real_coords->ydiff) * to_float(_real_coords->ydiff))); } /** * @param x1 the start x location * @param y1 the start y location * @param x1 the end x location * @param y1 the end y location */ void set_real_coordinates(int x1, int y1, int x2, int y2) { set_real_coordinates_no_inform(x1, y1, x2, y2); _real_coords->in_world_map = TERRAIN_MAP_WORLD_MAP->add_terrain(base_name(this_object()), _real_coords->x1, _real_coords->y1, _real_coords->x2, _real_coords->y2); } /** * This method sets the real offsets for movement in the rooms. This * is needed in rooms that do not have real co-ordinates set but still * require movement to generate the correct messages. * @param xoff how far you move in the x direction * @param yoff how far you move in the y direction * @param zoff how far you move in the z direction */ void set_real_offsets(int xoff, int yoff, int zoff) { _real_coords = new(class real_coords); _real_coords->xdiff = xoff; _real_coords->ydiff = yoff; _real_coords->xydiff = to_int(sqrt(to_float(_real_coords->xdiff) * to_float(_real_coords->xdiff)+ to_float(_real_coords->ydiff) * to_float(_real_coords->ydiff))); } /** * This method returns the room coordinates from the real coordinates. * @param x the x coordinate * @param y the y coordinate */ class coord query_terrain_from_real_coord(int x, int y, int z) { return new(class coord, x: (x - _real_coords->x1) / _real_coords->xdiff, y: (y - _real_coords->y1) / _real_coords->ydiff, z: z / _real_coords->zdiff ); } /** * This method returns the real coordinates from the terrain coordinates. * For simplicity we always assume the terrain sits at the water level. * @param x the x coordinate * @param y the y coordinate */ class coord query_real_from_terrain_coord(int x, int y, int z) { if (!_real_coords) { return new(class coord, x : 0, y : 0); } return new(class coord, x: _real_coords->x1 + x * _real_coords->xdiff, y: _real_coords->y1 + y * _real_coords->ydiff, z: z * _real_coords->zdiff ); } /** * @ignore */ int query_newline_mode() { return _newline_mode; } /** * This method sets the 'newline' mode for room description extras. * If set to 1, a newline will be inserted after all feature and * neighbouring room items. 1 is the default. * @param mode the new mode. */ void set_newline_mode(int mode) { _newline_mode = mode; } /** * This method sets the distance you can see in this terrain based on the * map. The distance is used to determine nice little map things to show * the players. * @param distance the distance you can see */ void set_visible_distance(int distance) { _visible_distance = distance; } /** * This method returns the distance that you can see in this terrain. * @return the visible distance in the terrain */ int query_visible_distance() { return _visible_distance; } /** * @ignore */ void create() { setup_area_map(); setup_handler(); } // Loads in the maps, and if the array of rooms hasn't been allocated, do so. /** * @ignore */ void setup_area_map() { int index; load_area_map(); load_zone_map(); if (!arrayp(_room_map)) { _room_map = ({ }); for (index = 0; index < _width; index++) _room_map += ({ allocate(_width) }); } } // Install a room in the room array, and set up the descs and // exits if need be. /** * Not needed. * @ignore void install_room(object room) { int *room_coords = room->query_terrain_coords(); int x = room_coords[0]; int y = room_coords[1]; if (!_room_map[y][x]) { _room_map[y][x] = room; } if (!room->query_installed()) { setup_room(room, "No base path"); } } */ /** * This method returns the region used when calculating the extents of the * feature. * @param name the name of the feature to find the region of * @return the feature region */ object query_feature_ob(string name) { return _features[name]->feature_ob; } // Calculate whether any of the features are visible from this room. // If so, put in the relevant descs and add_items. /** * @ignore * @return the blocking data */ mapping calc_features(object room) { class feature_data feature; string title; int *room_coords = room->query_terrain_coords(); int x = room_coords[0]; int y = room_coords[1]; int z = room_coords[2]; mapping data; string direc; int range; mapping block_feature; mixed* features; mixed* tmp; block_feature = ([ ]); features = ({ }); foreach(title, feature in _features) { if (!room->can_view_feature(title)) { continue; } if (!feature || !feature->feature_ob) { debug_printf("Unable to find feature object for %O\n", title); continue; } data = feature->feature_ob->query_feature_desc_from(x, y, z); if (!data || !sizeof(data)) { continue; } if (feature->feature_ob->query_blocking()) { foreach (direc, range in data) { if (!block_feature[direc]) { if (block_feature[direc] > range) { block_feature[direc] = range; } else { map_delete(data, direc); } } } } if (sizeof(data)) { features += ({ ({ title, feature, data }) }); } } foreach (tmp in features) { data = tmp[2]; // Check and see if we are blocked. if (sizeof(block_feature)) { foreach (direc, range in data) { if (block_feature[direc] && block_feature[direc] < range) { map_delete(data, direc); } } } if (sizeof(data)) { feature = tmp[1]; title = tmp[0]; room->add_feature(title, data, feature->f_items, feature->whole_sentance); if (!_feature_rooms[title]) { _feature_rooms[title] = ({ }); } else { _feature_rooms[title] = filter(_feature_rooms[title], (: $1 && objectp($1) :)); } _feature_rooms[title] |= ({ room }); } } } // See if this room has a signpost. If so, make it. /** * @ignore */ void add_signposts(object room) { class sign_data sign; int *coords = room->query_terrain_coords(); int x = coords[0]; int y = coords[1]; string title = sprintf("%d:%d", x, y); object sign_ob; //tell_creator("dek", "Looking for a sign: %s.\n", title); sign = _signposts[title]; if (!sign) { return; } sign_ob = room->add_sign(sign->long, sign->read_mess, sign->short, sign->name, sign->language); sign_ob->move(room); } // See what zones (if any) this room should belong to, and add them. /** * @ignore */ void setup_zones(object room) { string *zones; int *coords = room->query_terrain_coords(); int x = coords[0]; int y = coords[1]; string type; string zone; if (!sizeof(_zone_map)) { return; } type = _zone_map[y][x..x]; zones = _zone_types[type]; if (!zones) { return; } foreach (zone in zones) { room->add_zone(zone); } } void calc_random_descs(object room, string key) { int limit = _random_desc_limit; int *indices = ({ }); int *coords = room->query_terrain_coords(); int seed = (coords[0] * 2) + coords[1]; mapping desc; mixed *descs = _random_descs[key]; int index; if (limit > sizeof(descs)) { limit = sizeof(descs); } if (!limit) { return; } while (sizeof(indices) < limit) { index = RANDOM_HANDLER->random(sizeof(descs), seed++); if (member_array(index, indices) >= 0) { continue; } indices += ({ index }); } for (index = 0; index < limit; index++) { desc = descs[indices[index]]; room->add_random_desc(desc["desc"]); room->add_item(desc["items"], desc["item_desc"]); } } void calc_random_chats(object room, string key) { } /** * This method finds the co-ordinates to use for getting to the * next room. THis handles messing about to make bridges and things * @param x the x coordinate * @param y the y coordinate * @param z the z coordinate * @param direc the direction it is from * @param key the key is it from * @return the new mangled co-ordinates */ string query_dest_coordinate_string(int x, int y, int z, string direc, string key) { class char_map_data room_setup; room_setup = query_char_map(x, y); if (room_setup->type == AREA_WORLD) { return room_setup->path->query_dest_coordinate_string(x, y, z, direc, key); } return 0; } /** * This method returns the ground room for a specific co-ordinate. * @param x the x location * @param y the y location * @return the terrain co-ordinates of the ground room */ int* query_ground_room_co_ords(int x, int y) { class char_map_data room_setup; room_setup = query_char_map(x, y); return ({ x, y, room_setup->height }); } /** * This method returns the location of the ground room. It is basically * just the co-ordates connected onto the terrain_handler definition. * @param x the x location * @param y the y location * @return the string name of the ground room */ string query_ground_room(int x, int y) { int* co_ords; co_ords = query_ground_room_co_ords(x, y); return base_name(this_object()) + ":" + co_ords[0] + ":" + co_ords[1] + ":" + co_ords[2]; } /** * This method returns the basic room as the specified location. A climb * room type means a room that is up or down on the z axis from where we * are, but we are allowed to climb to. * @param x the x location * @param y the y location * @param z the z location * @param map_room check down through the air for the room for the map * @param base the updated base (for climbing rooms) * @return the basic room for the location */ string query_climb_base_room(int x, int y, int z, int map_room, string ref base) { class char_map_data room_setup; string *tmp; room_setup = query_char_map(x, y); if (!room_setup) { return 0; } if (room_setup->type == AREA_WORLD) { // Pass off the creation to the room file. if(file_size(room_setup->path + ".c") == -1) { tmp = explode(room_setup->path, "_edge"); if(sizeof(tmp) && file_size(tmp[0] + ".c") > 0) room_setup->path = tmp[0]; } return room_setup->path->find_base_terrain_room(x, y, z, room_setup->path, map_room, ref base); } else if (room_setup->height_rooms) { // Find the new room. if (z > room_setup->height) { if (map_room == TERRAIN_MAP_ROOM_MAP) { return room_setup->path; } if (map_room != TERRAIN_MAP_ROOM_CLIMB) { // air room. return TERRAIN_MAP_DEFAULT_AIR_ROOM; } return 0; } if (z == room_setup->height) { return room_setup->path; } if (room_setup->height_rooms[z]) { return room_setup->height_rooms[z]; } if (room_setup->height_rooms["minimum"] > z) { return 0; } return room_setup->height_rooms["default"]; } else if (z == room_setup->height) { return room_setup->path; } else if (map_room && z > room_setup->height) { return room_setup->path; } else if (z > room_setup->height) { if (map_room != TERRAIN_MAP_ROOM_CLIMB) { return TERRAIN_MAP_DEFAULT_AIR_ROOM; } } return 0; } /** * This returns the destination room in the given directoin, initially * it tries for a normal exit type. If that fails, it tries for a climbing * exit type. A climbing exit type is one that moves up or down from the * current z height. * @param x the x co-ordinate * @param y the y co-ordinate * @param z the z co-ordinate * @param base the base path of the parent terrain handler */ string query_base_room(int x, int y, int z, int map_room) { string dest; dest = "bing"; return query_climb_base_room(x, y, z, map_room, ref dest); } /** * The main nasty func for setting up a room. It adds all the necessary exits, * and adds to the room's extra_look & add_items for anything outside the room * which should be visible. It's fairly nasty. But hey - it works. */ void setup_room(object room, string base_path) { string main_base; string base; int *room_coords = room->query_terrain_coords(); mixed *coords; int x; int y; string direc; class char_map_data room_setup; string* dirs; mapping adjacent_descs = ([ ]); mapping adjacent_sent = ([ ]); mapping distant; string adjacent; string key; string room_long = ""; string key_here; mixed *exit_mods; object new_room; string outside_types = ""; mixed *exits; mapping exit; string *sentances; mixed tmp; int z; int road_jump; class coord fluff; string* miss_road; mapping ignore_exits; string walk_pre; string walk_full; string journey_pre; string new_base_path; if (!arrayp(room_coords)) { debug_printf("Unable to find room coordinates from %O\n", file_name(room)); return ; } room_setup = query_char_map(room_coords[0], room_coords[1]); key_here = room_setup->char; if (room_setup->room_desc) { adjacent_descs[room_setup->room_desc] = ({ "here" }); adjacent_sent[room_setup->char] = 1; if( room_setup->items ) { if( stringp( room_setup->items ) && stringp( room_setup->item_desc ) ) { room->add_item( room_setup->items, room_setup->item_desc ); } else if( arrayp( room_setup->items ) && arrayp( room_setup->item_desc ) && sizeof( room_setup->items ) ) { if( sizeof( room_setup->items ) != sizeof( room_setup->item_desc ) ) { debug_printf( "Error, unequal arrays. Item not set." ); } else { int i; for( i = 0; i < sizeof( room_setup->items ); i++ ) { room->add_item( room_setup->items[i], room_setup->item_desc[i] ); } } } } } if (room_setup->room_jump_matching) { road_jump = room_setup->room_jump; } walk_pre = room->query_terrain_map_walk_exit(); journey_pre = room->query_terrain_map_journey_exit(); exits = _standard_exits[sprintf("%d:%d:%d", room_coords[0], room_coords[1], room_coords[2])]; ignore_exits = ([ ]); if (exits) { foreach(exit in exits) { room->add_exit(exit["direc"], exit["dest"], exit["type"]); if (exit["modifiers"]) { room->modify_exit(exit["direc"], exit["modifiers"]); } ignore_exits[exit["direc"]] = 1; } } // // This is the start of the pre-caclulated exits for the terrain. // All the cardinal directions are checked for their ability to be // gone up and down. // Most checks are done on the base object (if possible) to avoid // loading everything in sight. // // Check the up and down exits first. main_base = base_name(this_object()); if (room_setup->height_rooms || room_setup->type == AREA_WORLD) { int i; x = room_coords[0]; y = room_coords[1]; // Ok, zip through this mapping and see if we match anything. // This finds if there is a room above or below us at all. for (i = 0; i < 2; i++) { z = room_coords[2]; if (!i) { direc = "up"; tmp = "down"; z++; } else { direc = "down"; tmp = "up"; z--; } base = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT); if (base) { // There is a room! Check and see if we can get in it or not. new_room = load_object(base); new_base_path = base; if (!new_room) { continue; } if (new_room->can_enter_from(key_here, direc, base_path) && room->can_exit_to(key_here, direc, new_base_path)) { room->add_exit(direc, main_base + ":" + x + ":" + y + ":" + z, "path"); exit_mods = room->get_to_same(tmp, direc, key, new_base_path); if (exit_mods) { room->modify_exit(direc, exit_mods); } } } } } // // This is to check for crossing over roads/rivers on a diagonal. // miss_road = allocate(3 * 3); // Check the other exits. foreach(direc in _direc_order) { coords = _direcs[direc]; x = coords[0] + room_coords[0]; y = coords[1] + room_coords[1]; z = room_coords[2]; if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) { // Check and see if we have an adjacent terrain or not base = find_adjacent_terrain(x, y); if (!base) { continue; } fluff = query_real_from_terrain_coord(x, y, z); fluff = base->query_terrain_from_real_coord(fluff->x, fluff->y, fluff->z); room_setup = base->query_char_map(fluff->x, fluff->y); if (room_setup && room_setup->type != AREA_OBSTACLE) { tmp = base->query_base_room(fluff->x, fluff->y, z, TERRAIN_MAP_ROOM_EXACT); if (tmp) { new_base_path = tmp; new_room = load_object(tmp); base += ":" + fluff->x + ":" + fluff->y + ":" + z; } else { new_room = 0; new_base_path = 0; } } } else { room_setup = query_char_map(x, y); if (room_setup && room_setup->type != AREA_OBSTACLE) { tmp = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT); new_base_path = tmp; if (tmp) { new_room = load_object(tmp); base = main_base + ":" + x + ":" + y + ":" + z; } else { new_room = 0; } } } if (!room_setup || !new_room) { continue; } key = room_setup->char; // Make sure it is cardinal. Check to see if we can leap this // location or not. // We use the file name of the new room to check for the diagonal // exit policy problems. This should solve the problems of moving // across maps. if (!coords[0] || !coords[1]) { miss_road[(coords[0] + 1) * 3 + coords[1] + 1] = new_base_path; } else { // Only do this if the values are not 0 too. if ((miss_road[(coords[0] + 1) * 3 + 1] != new_base_path || miss_road[coords[1] + 4] != new_base_path) && miss_road[coords[1] + 4] && miss_road[(coords[0] + 1) * 3 + 1] && !miss_road[coords[1] + 4]->can_move_between(key, direc, miss_road[(coords[0] + 1) * 3 + 1], miss_road[coords[1] + 4], new_base_path)) { // Don't allow this to happen. continue; } } switch(room_setup->type) { case AREA_OBSTACLE : break; case AREA_ROAD : case AREA_ROOM : case AREA_WORLD : if (ignore_exits[direc]) { break; } if (new_room->can_enter_from(key_here, direc, base_path) && room->can_exit_to(key, direc, new_base_path)) { // Now check and see if we need to modify the exit to this // room at all. tmp = new_room->query_enter_exit_direction(key, key_here, direc, room, base_path); if (tmp) { room->add_exit(tmp, base, "road"); walk_full = tmp; } else { // If they are not doing anything, setup all the default // hop stuff. room->add_exit(direc, base, "road"); room->add_exit(walk_pre + direc, base, "road"); room->add_default_exit(direc, base); walk_full = walk_pre + direc; } if (coords[4]) { room->modify_exit(walk_full, coords[4]); room->modify_exit(direc, coords[4]); } if (key_here == key || (road_jump && room_setup->room_jump_matching && room_setup->room_jump == road_jump)) { exit_mods = room->get_to_same(_direcs[direc][2], direc, key, new_base_path); if (exit_mods) { room->modify_exit(direc, exit_mods); room->modify_exit(walk_full, exit_mods); } if (room_setup->room_jump > 1) { //room->modify_exit(direc, ({ "obvious", 0 })); room->modify_exit(walk_full, ({ "obvious", 0 })); room->add_variable_exit(journey_pre + direc); room->add_exit(journey_pre + direc, base, "road"); exit_mods = room->get_to_same(walk_full, journey_pre + direc, key, new_base_path); if (coords[4]) { room->modify_exit(journey_pre + direc, coords[4]); } if (exit_mods) { room->modify_exit(journey_pre + direc, exit_mods); } } } else { exit_mods = room->get_to_other(_direcs[direc][2], direc, key, new_base_path); if (exit_mods) { room->modify_exit(walk_full, exit_mods); } exit_mods = new_room-> get_from_other(_direcs[direc][2], direc, key_here, base_path); if (exit_mods) { room->modify_exit(walk_full, exit_mods); } } room->modify_exit(direc, ({ "obvious", 0 })); } break; } if ((key_here != key) && (member_array(key[0], outside_types) < 0)) { outside_types += key; } adjacent = room_setup->adjacent_desc; if (adjacent && strlen(adjacent) > 1) { if (room->can_view_adjacent_desc(room_setup->char, key_here, room_setup->path, z, direc)) { if (!room_setup->whole_sentance) { adjacent_sent[adjacent] = 1; } if (!adjacent_descs[adjacent]) { adjacent_descs[adjacent] = ({ }); if( room_setup->items ) { if (!adjacent_sent[room_setup->char]) { adjacent_sent[room_setup->char] = 1; if( stringp( room_setup->items ) && stringp( room_setup->item_desc ) ) { room->add_item( room_setup->items, room_setup->item_desc ); } else if( arrayp( room_setup->items ) && arrayp( room_setup->item_desc ) && sizeof( room_setup->items ) ) { if( sizeof( room_setup->items ) != sizeof( room_setup->item_desc ) ) { debug_printf( "Error, unequal arrays. Item not " "set." ); } else { int i; for( i = 0; i < sizeof( room_setup->items ); i++) { room->add_item( room_setup->items[i], room_setup->item_desc[i] ); } } } } } } adjacent_descs[adjacent] += ({ direc }); } } } sentances = ({ }); foreach(adjacent, dirs in adjacent_descs) { tmp = replace_string(adjacent, "$D", query_multiple_short(dirs, "the", 1)); if (adjacent_sent[adjacent]) { if (strlen(tmp) > 1) { sentances += ({ tmp }); } } else if (tmp != "") { if (query_newline_mode()) { room_long += tmp + "\n"; } else { room_long += " " + tmp; } } } if (sizeof(sentances)) { if (!query_newline_mode()) { room_long += " "; } room_long += capitalize(query_multiple_short(sentances, "the", 1)) + "."; if (query_newline_mode()) { room_long += "\n"; } } room->set_outside_types(outside_types); add_signposts(room); setup_zones(room); // Do the features. exit = calc_features(room); if (_real_coords && (_real_coords->x1 || _real_coords->x2)) { fluff = query_real_from_terrain_coord(room_coords[0], room_coords[1], 0); foreach (string title in TERRAIN_MAP_WORLD_MAP->query_features_at(fluff->x, fluff->y)) { if (!room->can_view_feature(title)) { continue; } distant = title->query_feature_desc_from(fluff->x, fluff->y, fluff->z); if (sizeof(distant)) { // Check and see if the distant features are blocked. if (sizeof(exit)) { foreach (direc, int range in distant) { if (exit[direc] && exit[direc] < range) { map_delete(distant, direc); } } } if (sizeof(distant)) { room->add_distant_feature(title, distant); } } } } calc_random_descs(room, key_here); calc_random_chats(room, key_here); room->set_extra_long(room_long); room->set_installed(1); room->calc_exits(); } /** * THis method finds a terrain adjacent to us from this location * in the specific direction. * @param x the x location * @param y the y location */ string find_adjacent_terrain(int x, int y) { string index; class coord real; if (!_real_coords || !_real_coords->in_world_map) { return 0; } index = x + ":" + y; if (!undefinedp(_adjacent_terrains[index])) { return _adjacent_terrains[index]; } real = query_real_from_terrain_coord(x, y, 0); //printf("%O, %O) %O\n", x, y, real); // Ok, now see whayt we can find on the world map handler. _adjacent_terrains[index] = TERRAIN_MAP_WORLD_MAP->find_terrain_at(real->x, real->y); return _adjacent_terrains[index]; } /** * This is specifically for debug. It finds al the adjacent terrains. */ void find_all_adjacent_terrains() { find_adjacent_terrain(-1,-1); find_adjacent_terrain(-1,0); find_adjacent_terrain(-1,40); find_adjacent_terrain(0,40); find_adjacent_terrain(40,40); find_adjacent_terrain(40,0); find_adjacent_terrain(40,-1); find_adjacent_terrain(0,-1); } string* debug_adjacent_terrains() { return ({ find_adjacent_terrain(-1, 0), find_adjacent_terrain(0, -1), find_adjacent_terrain(0, _height), find_adjacent_terrain(_width, 0) }); } /** * This method find the specific squence of rooms in the given direction * with the given room jump. We do not worry about contents here. Only if * it is a special interest marker or if there are two different ways from * the location of the same type. Items in the map are set as being * special interest to stop a jump. * @param x the x co-ordinate to start from * @param y the y co-ordinate to start from * @return the list of rooms */ mixed* query_rooms_to(int x, int y, int z, string direc, int len, string main_key, int jump_len, int road_type ) { class char_map_data room_setup; class coord real; string new_direc; string found_direc; string terr; string bit; int num; int found; int old_x; int old_y; int new_x; int new_y; mixed* ret; mixed* data; ret = ({ ({ }), ({ }) }); do { data = _direcs[direc]; if (!data) { debug_printf("Bad direction %O\n", direc); continue; } old_x = x; old_y = y; x += data[0]; y += data[1]; if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) { terr = find_adjacent_terrain(x, y); if (!terr) { return ret; } real = query_real_from_terrain_coord(x, y, 0); data = terr->query_more_terrains_rooms_from(real->x, real->y, real->z, direc, len - num, jump_len, main_key, road_type); if (sizeof(data)) { ret[0] += data[0]; ret[1] += data[1]; } return ret; } ret[0] += ({ direc }); bit = sprintf("%d:%d:%d", x, y, z); ret[1] += ({ sprintf("%s:%s", base_name(this_object()), bit) }); // Always stop if there is an exit forced in the room. if (_standard_exits[bit]) { break; } if (road_type) { // Search around for the next location. found = 0; foreach (new_direc, data in _direcs) { new_x = x + data[0]; new_y = y + data[1]; if (new_x != old_x || new_y != old_y) { if ((new_x < 0) || (new_x >= _width) || (new_y < 0) || (new_y >= _height)) { terr = find_adjacent_terrain(new_x, new_y); if (terr) { real = query_real_from_terrain_coord(new_x, new_y, z); real = terr->query_terrain_from_real_coord(real->x, real->y, z); room_setup = terr->query_char_map(real->x, real->y); } else { room_setup = 0; } } else { room_setup = query_char_map(new_x, new_y); } if (room_setup && room_setup->room_jump == jump_len) { //printf("Found! %O %O %O\n", new_direc, room_setup->room_jump, jump_len); found_direc = new_direc; found++; } } } //printf("(%O, %O) %O %O %O\n", x, y, found, found_direc, jump_len); if (found == 1) { direc = found_direc; } else { // dead end or junction. break; } } else { room_setup = query_char_map(x + data[0], y + data[1]); if (!room_setup || room_setup->char != main_key) { break; } } } while (num++ < len); return ret; } /* query_rooms_to() */ /** * This method is used by connecting terrains to find the next room in * sequence. * @param x the real x coordinate * @param y the real y coordinate * @param moves_left the number of moves left to pop along * @param jump the jump length we are looking for * @param main_key the key, this is only used for non-road jumps * @param road_jump is this a road jump */ mixed* query_more_terrains_rooms_from(int x, int y, int z, string direc, int moves_left, int jump, string main_key, int road_jump) { class coord bing; class char_map_data room_setup; int found; string found_direc; string new_direc; mixed* data; int new_x; int new_y; //tell_creator("pinkfish", "%O: %O %O %O %O %O\n", base_name(this_object()), x, y, direc, road_jump, main_key); bing = query_terrain_from_real_coord(x, y, 0); room_setup = query_char_map(bing->x, bing->y); if (road_jump) { // Is the jump value set correctly here? if (room_setup->room_jump != jump) { data = _direcs[direc]; bing->x -= data[0]; bing->y -= data[1]; // Try and find it from here. We move away from the edge of the // terrain and search from there (the room we came from). foreach (new_direc, data in _direcs) { new_x = bing->x + data[0]; new_y = bing->y + data[1]; room_setup = query_char_map(new_x, new_y); if (room_setup && room_setup->room_jump == jump) { found = 1; found_direc = new_direc; break; } } if (!found) { return ({ }); } direc = found_direc; } else { data = _direcs[direc]; bing->x -= data[0]; bing->y -= data[1]; } return query_rooms_to(bing->x, bing->y, bing->z, direc, moves_left, main_key, jump, road_jump); } else { // Find where we are. if (room_setup->char == main_key) { data = _direcs[direc]; bing->x -= data[0]; bing->y -= data[1]; return query_rooms_to(bing->x, bing->y, bing->z, direc, moves_left, main_key, jump, road_jump); } return ({ }); } } /** * This method finds the next room in the path. It returns the next * room as the first element in the array and the list of the directions * to get there as the second element. * @param x the start x position * @param y the start y position * @param z the start z position * @param direc the direction to go from here * @return ({ the end location, directions... }) */ string* find_next_room_from(int x, int y, int z, string direc) { class char_map_data room_setup; string test; object ob; object* obs; mixed* stuff; int pos; //direc = direc[strlen(SKIP_EXIT_PRE)..]; room_setup = query_char_map(x, y); stuff = query_rooms_to(x, y, z, direc, room_setup->room_jump, room_setup->char, room_setup->room_jump, room_setup->room_jump_matching); foreach (test in stuff[0]) { ob = find_object(test); if (ob) { obs = filter(all_inventory(ob), (: living($1) :)); if (sizeof(obs) > 0) { return ({ test }) + stuff[0..pos]; } } pos++; } if (sizeof(stuff[1])) { return ({ stuff[1][<1] }) + stuff[0]; } return 0; } /* find_next_room_from() */ // Called by the room-inherit when a room has a zone added. Lets us maintain // a list of what rooms are in what zones, so we can do snazzy things with them. /** * @ignore */ void add_room_to_zone(object room, string zone) { object *zones = _room_zones[zone]; if (!zones) zones = ({ room }); else if (member_array(room, zones) == -1) zones += ({ room }); _room_zones[zone] = zones - ({ 0 }); } /** * This method queries the path of a room. If it takes the form: * '/d/full/path/name.c:x:y' where x and y are integers, it clones * a new room at the specified coordinates, sets it up fully, and returns it. * If the room path is of the standard form, it simply returns the loaded * room. * @param room_path the full pathname of the room to be cloned. * @return the cloned/loaded room, or 0 if it could not be cloned/loaded * @see load_room_at * @see query_room_at */ mixed query_room(int x, int y, int z) { class char_map_data room_map = query_char_map(x, y); string base_path; object room; class coord real; base_path = query_base_room(x, y, z, TERRAIN_MAP_ROOM_EXACT); if (!base_path) { return 0; } // // Not in the driver mapping, so we must need to create it. // We assume the external stuff is doing the correct name stuff. // room = clone_object(base_path); if (!room) { debug_printf("Unable to create room from %O at %O %O %O\n", base_path, x, y, z); } else { room->create_terrain_room(); room->set_terrain_coords(x, y, z); real = query_real_from_terrain_coord(x, y, z); room->set_co_ord(({ real->x, real->y, real->z })); if (classp(_real_coords)) { room->set_room_size(({ _real_coords->xdiff, _real_coords->ydiff, _real_coords->zdiff })); } room->set_terrain_handler(base_name(this_object())); room->terrain_setup(); room->add_property("base path", base_path); _room_map[y][x] = room; setup_room(room, base_path); if (room_map->type == AREA_WORLD && base_path != room_map->path) { // Use this to do any useful special stuff (like river flows). room_map->path->setup_in_terrain(room, x, y, z); } } return room; } /** * This method is used to find a room at the specified coordinates, * if one has already been cloned. * If a room has been cloned at those coordinates, it is returned, * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @return the room at those coordinates, or 0 if none has been cloned * @see load_room_at * @see query_room * @see query_rooms_in_block * @see query_rooms_in_range */ object query_room_at(int x, int y, int z) { if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0; return find_object(sprintf("%s:%d:%d:%d", base_name(this_object()), x, y, z)); } /** * This method is used to find all the rooms currently cloned within a * specified rectangular area. * @param x the x coordinate of rectangle's bottom left corner * @param y the y coordinate of rectangle's bottom left corner * @param width the width of the rectangle * @param height the height of the rectangle * @return an array containing any rooms currently cloned within the * specified rectangle * @see query_room_at * @see query_rooms_in_range * @see query_rooms_in_zone */ object *query_rooms_in_block(int x, int y, int z, int width, int height) { int h, v; mixed *result = ({ }); object room; for (h = x; h < (x + width); h++) { for (v = y; v < (y + height); v++) { room = query_room_at(h, v, z); if (room) result = result + ({ room }); } } return result; } /** * This method is used to find all the rooms currently cloned within a * specified distance of the specified coordinates. * @param x the x coordinate of the center point * @param y the y coordinate of the center point * @param max the maximum distance of rooms to be returned * @param min the minimum distance of rooms to be returned. Defaults to 0 * @return an array containing any rooms currently cloned within the * specified range * @see query_room_at * @see query_rooms_in_block * @see query_rooms_in_zone */ varargs object *query_rooms_in_range(int x, int y, int z, int max, int min) { int h, v; mixed *result = ({ }); object room; int dist; for (h = x - max; h <= (x + max); h++) { for (v = y - max; v <= (y + max); v++) { dist = to_int(sqrt(pow(x - h, 2) + pow(y - v, 2)) + 0.4999); // tell_creator("dek", "Coords: (%d, %d), Range: %d, Dist: %d\n", h, v, max, dist); if ((dist > max) || (dist < min)) continue; room = query_room_at(h, v, z); if (room) result = result + ({ room }); } } return result; } /** * This method is used to find a room at the specified coordinates, * or to clone and setup a new one if none exists so far. * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @return the room at those coordinates, or 0 if none can be cloned * @see load_room_at * @see query_room */ object load_room_at(int x, int y, int z) { class char_map_data room_map = query_char_map(x, y); if (!room_map) { debug_printf("Unable to load room at %d %d\n", x, y); return 0; } if (room_map->type == AREA_OBSTACLE) { return 0; } return load_object(sprintf("%s:%d:%d:%d", base_name(this_object()), x, y, z)); } // Given coordinates, get the mapping which controls what goes in the room. /** * @ignore */ class char_map_data query_char_map(int x, int y) { string type; class char_map_data room_map; if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) { return 0; } if (x >= sizeof(_area_map[y])) { debug_printf("The x value is out of range: %d > %d", x, sizeof(_area_map[y])); return 0; } type = _area_map[y][x..x]; room_map = copy(_char_map[type]); if (!room_map) { debug_printf("There is no char type for '%s'", type); return 0; } room_map->char = type; return room_map; } /** * This method returns a grid of the specified size around the center * point to give creators an overview of where they are currently. * @param x the x co-ordinate * @param y the y co-ordinate * @param width the width of the box */ string query_debug_map(int x, int y, int width, int showx, int showy) { int i; int j; int sx; int sy; int ny; string ret; string base; string new_base; string colour; ret = ""; sx = x - width / 2; sy = y + width / 2; for (j = 0; j < width; j++) { if (sy - j >= 0 && sy - j < _height) { for (i = 0; i < width; i++) { if (sx + i >= 0 && sx + i < _width) { if (sx +i == showx && sy - j == showy) { ret += "%^YELLOW%^" + _area_map[sy - j][sx + i..sx + i] + "%^RESET%^"; colour = 0; } else { if (colour) { ret += "%^RESET%^"; colour = 0; } ret += _area_map[sy - j][sx + i..sx + i]; } } else { // Need to get the character from a nearby terrain instead. if (sx + i >= _width) { base = find_adjacent_terrain(_width + 1, sy - j); } else { base = find_adjacent_terrain(-1, sy - j); } //printf("Base %O (%d,%d)\n", base, sx + i, sy - j); if (base) { //ret += "-"; if (colour != "%^CYAN%^") { ret += "%^BOLD%^%^CYAN%^"; colour = "%^CYAN%^"; } if (sx + i >= _width) { //ret += "-" + (sx + i - _width) + "-"; // Assume same size. ret += base->query_debug_map_char(sx + i - _width, sy - j); } else { //ret += "-" + (sx + i + _width) + "-"; ret += base->query_debug_map_char(sx + i + _width, sy - j); } } else { ret += " "; } } } if (colour) { ret += "%^RESET%^"; colour = 0; } ret += "\n"; } else { // Need to get the character from a nearby terrain instead. base = find_adjacent_terrain(sx + i, sy - j); if (base) { if (sy - j < 0) { ny = sy - j + _height; } else { ny = sy - j - _height; } for (i = 0; i < width; i++) { if (sx + i >= 0 && sx + i < _width) { if (colour != "%^MAGENTA%^") { ret += "%^BOLD%^%^MAGENTA%^"; colour = "%^MAGENTA%^"; } ret += base->query_debug_map_char(sx + i, ny); } else { new_base = find_adjacent_terrain(sx + i, sy - j); if (new_base) { if (colour != "%^RED%^") { ret += "%^BOLD%^%^RED%^"; colour = "%^RED%^"; } if (sx + i >= _width) { // Assume same size. ret += new_base->query_debug_map_char(sx + i - _width, ny); } else { ret += new_base->query_debug_map_char(sx + i + _width, ny); } } else { ret += " "; } } } ret += "%^RESET%^\n"; colour = 0; } } } return ret; } /** * This method returns the specific debug map character at the * given location. * @param x the x location * @param y the y location * @return the map character */ string query_debug_map_char(int x, int y) { return _area_map[y][x..x]; } /** * This method returns a grid of the specified size around the center * point to give creators an overview of where they are currently. * @param x the x co-ordinate * @param y the y co-ordinate * @param width the width of the box */ string query_debug_map_feature(string name) { int x; int y; class feature_data feature; mixed data; string ret; int colour; int found; //string direc; //int distance; feature = _features[name]; ret = ""; colour = 0; for (y = _height - 1; y >= 0; y--) { for (x = 0; x < _width; x++) { data = feature->feature_ob->query_feature_desc_from(x, y, 0, 1); found = sizeof(data); if (found) { if (!colour) { ret += "%^YELLOW%^"; colour = 1; } } else { if (colour) { ret += "%^RESET%^"; colour = 0; } } ret += _area_map[y][x..x]; } ret += "%^RESET%^\n"; colour = 0; } return ret; } /** * This method returns a grid of the specified size around the center * point to give creators an overview of where they are currently. * @param x the x co-ordinate * @param y the y co-ordinate * @param width the width of the box */ string query_debug_map_feature_distant(string feature) { int x; int y; mixed data; string ret; int colour; class coord fluff; ret = ""; colour = 0; for (y = _height - 1; y >= 0; y--) { for (x = 0; x < _width; x++) { fluff = query_real_from_terrain_coord(x, y, 0); data = feature->query_feature_desc_from(fluff->x, fluff->y, fluff->z); if (data && sizeof(data)) { if (!colour) { ret += "%^YELLOW%^"; colour = 1; } } else { if (colour) { ret += "%^RESET%^"; colour = 0; } } ret += _area_map[y][x..x]; } ret += "%^RESET%^\n"; colour = 0; } return ret; } /** * This adds the new string to the key. */ private void add_key(mapping key, string char, string str) { if(strsrch(str, "intersection") != -1) return; if(!key[char]) key[char] = str; else if(strsrch(key[char], str) == -1 ) { key[char] += ", " + str; } } /** * This updates the map and makes the data. */ private int update_map(mapping data, mixed* map, mapping key, mixed *colours, int x, int y, int z, int offx, int offy) { class char_map_data info; string char, colour, tmp; string room; int block; object ob; if (x < 0) { if (y < 0) { data["-y-x"] = 1; } else if (y >= _height) { data["+y-x"] = 1; } else { data["-x"] = 1; } return 0; } else if (x >= _width) { if (y < 0) { data["-y+x"] = 1; } else if (y >= _height) { data["+y+x"] = 1; } else { data["+x"] = 1; } return 0; } else if (y < 0) { data["-y"] = 1; return 0; } else if (y >= _height) { data["+y"] = 1; return 0; } info = query_char_map(x, y); if (!info) { return 1; } if (info->type == AREA_OBSTACLE) { map[y - offy][x - offx] = info->char; colours[y - offy][x - offx] = info->colour; if(!info->colour) { colours[y - offy][x - offx] = "%^BLUE%^"; } if (info->room_desc == "Obstacle" && stringp(info->items)) { add_key(key, info->char, info->items); } else if (info->room_desc != "") { add_key(key, info->char, info->room_desc); } else { map[y - offy][x - offx] = " "; } data["block" + info->char] = 1; return 1; } else { char = data[info->char]; colour = data[info->colour]; if (!char || !colour) { room = query_base_room(x, y, z, TERRAIN_MAP_ROOM_MAP); if (room) { ob = load_object(room); if (ob) { char = ob->query_terrain_map_character(); colour = ob->query_terrain_map_colour(); block = ob->query_terrain_map_block(); if(char && ob) { tmp = ob->query_key_desc(); if(!tmp) { tmp = ob->query_short(); } if(!tmp) { debug_printf("Room %s has no short.", base_name(ob)); } else { add_key(key, char, tmp); } } if (block || info->height > z) { data["block" + info->char] = 1; } } else { debug_printf("Unable to find %O\n", room); } } if (!char) { char = info->char; } if(!colour) { colour = info->colour; } data[info->char] = char; } map[y - offy][x - offx] = char; colours[y - offy][x - offx] = colour; } if(char && !key[char] && !data["nokey"+info->char]) { data["nokey" + info->char] = 1; debug_printf("No key for %s", char); } return data["block" + info->char]; } /** * This method fills in the line with stuff if it is needed. * @param map the map to fill in * @param sx the startx * @param sy the starty * @param width the width of the map */ private string line_in_map(mapping data, mixed* map, mapping key, mixed *colours, int offx, int offy, int sx, int sy, int ex, int ey, int z) { int x; int y; int dx; int dy; int incx; int incy; int balance; if (ex >= sx) { dx = ex - sx; incx = 1; } else { dx = sx - ex; incx = -1; } if (ey >= sy) { dy = ey - sy; incy = 1; } else { dy = sy - ey; incy = -1; } x = sx; y = sy; if (dx >= dy) { dy <<= 1; balance = dy - dx; dx <<= 1; while (x != ex) { if (update_map(data, map, key, colours, x, y, z, offx, offy)) { break; } if (balance >= 0) { y += incy; balance -= dx; } balance += dy; x += incx; } } else { dx <<= 1; balance = dx - dy; dy <<= 1; while (y != ey) { if (update_map(data, map, key, colours, x, y, z, offx, offy)) { break; } if (balance >= 0) { x += incx; balance -= dy; } balance += dx; y += incy; } } // Last pixel... update_map(data, map, key, colours, x, y, z, offx, offy); } /** * Updates the blocking structures. */ private void update_blocking(mapping data, mixed* map, mixed* colours, int x, int y, int distance) { int dx; int dy; int adx; int ady; int dist; int nx; int ny; int tx; int ty; int incx; int incy; string* template; string str; dx = (x - distance); dy = (y - distance); //str = sprintf("%d", x % 10); str = " "; if (dx < 0) { adx = - dx; incx = -1; } else { adx = dx; incx = 1; } if (dy < 0) { ady = - dy; incy = -1; } else { ady = dy; incy = 1; } if (incx != incy) { incx = -incx; incy = - incy; } if (adx > ady) { template = TERRAIN_MAP_WORLD_MAP->query_blocking_template(ady, adx, 0); } else { template = TERRAIN_MAP_WORLD_MAP->query_blocking_template(adx, ady, 0); } // Error. if (!template) { debug_printf("Error with the template"); return ; } dist = distance + 1; for (tx = 0; tx < dist; tx++) { for (ty = 0; ty < dist; ty++) { if (template[tx][ty] == '*') { // Zap the structures. if (adx > ady) { nx = distance + tx * incx; ny = distance + ty * incy; } else { nx = distance + ty * incx; ny = distance + tx * incy; } if (nx < sizeof(map) && ny < sizeof(map[nx]) && nx >= 0 && ny >= 0) { map[nx][ny] = str; colours[nx][ny] = 0; } if (adx == ady) { nx = distance + tx * incx; ny = distance + ty * incy; map[nx][ny] = str; colours[nx][ny] = 0; } else if (adx == 0) { nx = distance + ty * incx; ny = distance - tx * incy; map[nx][ny] = str; } else if (ady == 0) { nx = distance - tx * incx; ny = distance + ty * incy; map[nx][ny] = str; colours[nx][ny] = 0; } } } } } /** * This method does the updating of the map based on the template. * Pretty easy really, check each location in the map to see if it * exists and what we should do with it. */ mapping query_player_map_internal_template(mixed* map, mapping key, mixed *colours, int x_c, int y_c, int z_c, int distance) { int offx; int offy; int x; int y; mapping data; data = ([ ]); offx = x_c - distance; offy = y_c - distance; // Go from the top corner to the bottom corner. for (x = 0; x < sizeof(map); x++) { for (y = 0; y < sizeof(map[x]); y++) { if (map[y][x] == 1) { // Update it. if (update_map(data, map, key, colours, x + offx, y + offy, z_c, offx, offy)) { // Need to update the blocking. // Check for diagonals and blocking. if (x > 0) { if (y > 0 && data["block" + map[y-1][x-1]]) { update_blocking(data, map, colours, x, y, distance); update_blocking(data, map, colours, x, y-1, distance); //map[y-1][x] = "#"; } else if (y < sizeof(map) - 1 && data["block" + map[y+1][x-1]]) { update_blocking(data, map, colours, x, y, distance); update_blocking(data, map, colours, x, y + 1, distance); //map[y+1][x] = "#"; } else if (y > 0 && data["block" + map[y-1][x]]) { update_blocking(data, map, colours, x, y, distance); update_blocking(data, map, colours, x - 1, y - 1, distance); } else { update_blocking(data, map, colours, x, y, distance); } } else { update_blocking(data, map, colours, x, y, distance); } } } } } return data; } /** * This method generates a player map using templates instead of using * lines from the center point. This is more efficent than a * line drawing method. THe max_size is used to * make sure the size of the visible area is squashed, if we want a * smaller partial map for other displays. The max size must be * an odd number. */ string query_player_map_template(int x_c, int y_c, int z_c, int visibility, int max_size) { string ret, item, base, current, *keys; mixed *map, *colours; mapping data, key; int i, j; int distance; //class char_map_data tmp; distance = _visible_distance * visibility / 100; if (!distance) { distance = 1; } data = ([ ]); key = ([ ]); map = ({ }); colours = ({ }); // Returns a template for the given visiblity distance. map = TERRAIN_MAP_WORLD_MAP->query_map_template(distance); colours = TERRAIN_MAP_WORLD_MAP->query_map_template(distance); // Slice the map down to size. if (max_size && (max_size % 2) != 1) { max_size--; } if (max_size && sizeof(map) > max_size) { j = (max_size - 1) / 2; map = map[distance - j..distance + j]; colours = colours[distance - j..distance + j]; for (i = 0; i < sizeof(map); i++) { map[i] = map[i][distance - j..distance + j]; colours[i] = colours[i][distance - j..distance + j]; } // Need to mess with x_c and y_c x_c += (distance - j); y_c += (distance - j); } data = query_player_map_internal_template(map, key, colours, x_c, y_c, z_c, distance); if (data["-y"]) { base = find_adjacent_terrain(x_c, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c, y_c + _height, z_c, distance); } } } if (data["+y"]) { base = find_adjacent_terrain(x_c, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c, y_c - _height, z_c, distance); } } } if (data["-x"]) { base = find_adjacent_terrain(-1, y_c); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c + _width, y_c, z_c, distance); } } } if (data["+x"]) { base = find_adjacent_terrain(_width + 1, y_c); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c - _width, y_c, z_c, distance); } } } if (data["-y+x"]) { base = find_adjacent_terrain(_width + 1, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c - _width, y_c + _height, z_c, distance); } } } if (data["+y+x"]) { base = find_adjacent_terrain(_width + 1, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c - _width, y_c - _height, z_c, distance); } } } if (data["+y-x"]) { base = find_adjacent_terrain(-1, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c + _width, y_c - _height, z_c, distance); } } } if (data["-y-x"]) { base = find_adjacent_terrain(-1, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal_template(map, key, colours, x_c + _width, y_c + _height, z_c, distance); } } } map[sizeof(map) / 2][sizeof(map) / 2] = "@"; if(!this_player()->query_property(PLAYER_PLAIN_MAPS)) { colours[sizeof(map) / 2][sizeof(map) / 2] = "%^YELLOW%^"; for(i=0; i<sizeof(map); i++) { current = ""; for(j=0; j<sizeof(map); j++) { if(map[i][j] == "\r" || !stringp(map[i][j])) { map[i][j] = " "; } if (!current || current == " ") { current = ""; } if(!colours[i][j] || colours[i][j] == " ") { colours[i][j] = ""; } if(colours[i][j] != current) { if (colours[i][j] == "" || !stringp(colours[i][j])) { if(current != "" && current != " ") { map[i][j] = "%^RESET%^" + map[i][j]; current = ""; } } else if (colours[i][j] != "\r") { map[i][j] = colours[i][j] + map[i][j]; current = colours[i][j]; } } //map[i][j] = "[" + map[i][j] + "]"; if(i == x_c && j == y_c && current != "") { map[i][j] += current; } } if(current) { map[i][j-1] += "%^RESET%^"; } } } //ret = replace(ret, "%^", ""); if(_key && !max_size) { keys = ({ }); foreach(item in keys(key)) { keys += map(explode(sprintf("%s %-=40s", item, key[item]), "\n"), (: " " + $1 :)); } } ret = ""; for(i = sizeof(map) - 1; i >= 0; i--) { ret += implode(map(map[i], (: stringp($1) ? $1 : " " :)), ""); if(!max_size && sizeof(map)-2-i>= 0 && sizeof(map)-2-i < sizeof(keys)) { ret += keys[sizeof(map)-2-i]; } ret += "\n"; } return ret; } /** * This updates the player map based on the input. * @ignore yes */ mapping query_player_map_internal(mixed* map, mapping key, mixed *colours, int x_c, int y_c, int z_c, int distance) { int x, y, e, u, v; int endx, endy, offx, offy; mapping data; data = ([ ]); offx = x_c - distance; offy = y_c - distance; x = 0; y = distance; u = 1; v = 2 * distance - 1; e = 0; while (x < y) { // point 1. endx = x_c + x; endy = y_c + y; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 2. endx = x_c + y; endy = y_c - x; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 3. endx = x_c - x; endy = y_c - y; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 4. endx = x_c - y; endy = y_c + x; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); x++; e += u; u += 2; if (v < 2 * e) { y--; e -= v; v -= 2; } if (x > y) { break; } // point 5. endx = x_c + y; endy = y_c + x; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 6. endx = x_c + x; endy = y_c - y; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 7. endx = x_c - y; endy = y_c - x; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); // point 8. endx = x_c - x; endy = y_c + y; line_in_map(data, map, key, colours, offx, offy, x_c, y_c, endx, endy, z_c); } // The algorithm for a circle misses a few so we'll fill them in here. // The "gap" rooms always have filled-in locations on either side so // test for that condition and fill-in as necessary. for(y=0; y<sizeof(map); y++) for(x=1; x<sizeof(map[y])-1; x++) if(map[y][x] == "\r" && map[y][x-1] != "\r" && map[y][x+1] != "\r") { update_map(data, map, key, colours, x + offx, y + offy, z_c, offx, offy); } return data; } /** * This method returns the player line of sight view of the current terrain. * It depends on height and other wonderful things. * @param x_c the x center point * @param y_c the y center point * @param z_c the z center point * @param visibility the visibility percentage currently */ string query_player_map(int x_c, int y_c, int z_c, int visibility) { string ret, item, base, current, *keys; mixed *map, *colours; mapping data, key, colour_key; int i, j; int distance; //class char_map_data tmp; distance = _visible_distance * visibility / 100; if (!distance) { distance = 1; } data = ([ ]); key = ([ ]); map = ({ }); colours = ({ }); for (i = 0; i < distance * 2 + 1; i++) { map += ({ allocate(distance * 2 + 1, "\r") }); colours += ({ allocate(distance * 2 + 1, "\r") }); } data = query_player_map_internal(map, key, colours, x_c, y_c, z_c, distance); if (data["-y"]) { base = find_adjacent_terrain(x_c, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c, y_c + _height, z_c, distance); } } } if (data["+y"]) { base = find_adjacent_terrain(x_c, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c, y_c - _height, z_c, distance); } } } if (data["-x"]) { base = find_adjacent_terrain(-1, y_c); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c + _width, y_c, z_c, distance); } } } if (data["+x"]) { base = find_adjacent_terrain(_width + 1, y_c); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c - _width, y_c, z_c, distance); } } } if (data["-y+x"]) { base = find_adjacent_terrain(_width + 1, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c - _width, y_c + _height, z_c, distance); } } } if (data["+y+x"]) { base = find_adjacent_terrain(_width + 1, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c - _width, y_c - _height, z_c, distance); } } } if (data["+y-x"]) { base = find_adjacent_terrain(-1, _height + 1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c + _width, y_c - _height, z_c, distance); } } } if (data["-y-x"]) { base = find_adjacent_terrain(-1, -1); if (base) { if (!load_object(base)) { debug_printf("Unable to load %O\n", base); } else { base->query_player_map_internal(map, key, colours, x_c + _width, y_c + _height, z_c, distance); } } } y_c -= y_c - distance; x_c -= x_c - distance; if(!this_player()->query_property(PLAYER_PLAIN_MAPS)) { colour_key = ([ ]); map[y_c][x_c] = "%^YELLOW%^@%^RESET%^"; // This is going to go through the 2d map array and put in colour and // reset codes as necessary. for(i=0; i<sizeof(map); i++) { current = ""; for(j=0; j<sizeof(map); j++) { if(map[i][j] == "\r") map[i][j] = " "; if(!current || current == " ") current = ""; if(!colours[i][j] || colours[i][j] == " ") colours[i][j] = ""; // Make a colour coded map. if(key[map[i][j]]) colour_key[colours[i][j] + map[i][j] + "%^RESET%^"] = key[map[i][j]]; if(colours[i][j] != current) { if(colours[i][j] == "") { if(current != "" && current != " ") { map[i][j] = "%^RESET%^" + map[i][j]; current = ""; } } else if(colours[i][j] != "\r") { map[i][j] = colours[i][j] + map[i][j]; current = colours[i][j]; } } //map[i][j] = "[" + map[i][j] + "]"; if(i == x_c && j == y_c && current != "") map[i][j] += current; } if(current) map[i][j-1] += "%^RESET%^"; } } else { map[y_c][x_c] = "@"; colour_key = key; } if(_key) { keys = ({ }); foreach(item in keys(colour_key)) { keys += map(explode(sprintf("%s %-=40s", item, colour_key[item]), "\n"), (: " " + $1 :)); } } ret = ""; for(i = sizeof(map) - 1; i >= 0; i--) { ret += implode(map[i], ""); if(sizeof(map)-2-i>= 0 && sizeof(map)-2-i < sizeof(keys)) ret += keys[sizeof(map)-2-i]; ret += "\n"; } // This is normally done in the colour section. if(this_player()->query_property(PLAYER_PLAIN_MAPS)) ret = replace_string(ret, "\r", " "); return ret; } /** * This is used by the terrain system to work out what is beside * us. * @param x the x position * @param y the y position */ class char_map_data query_char_map_real_coord(int x, int y) { class coord our; our = query_terrain_from_real_coord(x, y, 0); return query_char_map(our->x, our->y); } /** * This method is used to find all rooms currently loaded within the * specified room zone. * @param zone the name of the room zone * @return an array containing all the currently cloned rooms within this zone * @see query_rooms_in_block * @see query_rooms_in_range */ object *query_rooms_in_zone(string zone) { if (_room_zones[zone]) { _room_zones[zone] -= ({ 0 }); return _room_zones[zone]; } return ({ }); } /** * This method displays a message in all the currently loaded rooms * within the specified zone(s). * @param zones a zone name as a string, or an array of zone names * @param message the message to be displayed * @param mess_class the message type. Normally this need not be specified * @see query_rooms_in_zone * @see tell_feature */ varargs void tell_zones(mixed zones, string message, mixed mess_class) { object *rooms = ({ }); string zone; if (!mess_class) mess_class = "zone_shout"; if (stringp(zones)) zones = ({ zones }); foreach (zone in zones) rooms += query_rooms_in_zone(zone); if (sizeof(rooms)) message(mess_class, message, rooms); } /** * This message displays messages in all rooms within range of a feature. * The message can be either a string, which goes to all the rooms, or an * array of strings, in which case they get spread across the distances (in * which case you should supply the same number of array elements as in the * descriptions in the original add_feature. Any occurences of $D in the * message(s) will be replaced in each room with the direction that the * feature lies in. * @param title the name of the feature, as used in add_feature * @param message the message to be displayed * @see tell_rooms_in_zone * @see add_feature * @see modify_feature * @see set_feature_chats */ varargs void tell_feature(string title, mixed message) { int index; string mess_class = sprintf("feature:%s", title); if (!_feature_rooms[title]) { return; } _feature_rooms[title] -= ({ 0 }); if (!sizeof(_feature_rooms[title])) { return; } if (stringp(message)) { message(mess_class, message, _feature_rooms[title]); } if (arrayp(message)) { for (index = 0; index < sizeof(message); index++) { message(sprintf("%s:%d", mess_class, index), message[index], _feature_rooms[title]); } } } // Called by rooms to get the feature description that applies to them. // It'll do direction substitution if it's supplied with a direc to subs. /** * @ignore */ varargs string* query_distant_feature_desc(string title, mapping direcs, int visibility) { int night = (WEATHER->query_tod() == "night"); string str; str = title->calc_feature_desc(direcs, night, visibility); if (str) { return ({ str }); } return ({ }); } // Called by rooms to get the item desc of a feature. // It'll do direction substitution if it's supplied with a direc to subs. /** * @ignore */ varargs string* query_feature_desc(string title, mapping direcs, int visibility) { class feature_data feature; string* item_desc; string* dirs; int night = (WEATHER->query_tod() == "night"); string direc; int range; mapping bits; int index; string str; int new_range; if (!feature) { feature = _features[title]; } if (!feature) { return ({ }); } if( visibility == 0 ) { return ({ }); } if (!sizeof(feature->descs)) { str = feature->feature_ob->calc_feature_desc(direcs, night, visibility); if (str) { return ({ str }); } return ({ }); } bits = ([ ]); foreach (direc, range in direcs) { if (night) { item_desc = feature->night_descs; } else { item_desc = feature->descs; } if (!item_desc) { debug_printf("Bad night description for %O", title); continue; } new_range = range * 100 / visibility; // Make sure the modified range is less the visibility if (feature->range > new_range && sizeof(item_desc)) { index = sizeof(item_desc) * new_range / feature->range; str = item_desc[index]; if (str && str != "") { if (!bits[str]) { bits[str] = ({ }); } bits[str] += ({ direc }); } } } item_desc = ({ }); foreach (str, dirs in bits) { item_desc += ({ replace_string(str, "$D", query_multiple_short(dirs)) }); } return item_desc; } /** * This method returns the item description. * @param title the title of the feature * @return the feature item description */ varargs string query_feature_item_desc(string title) { class feature_data feature; mixed item_desc; int night = (WEATHER->query_tod() == "night"); if (!feature) { feature = _features[title]; } if (!feature) { return ""; } if (night) { item_desc = feature->f_item_night_desc; } else { item_desc = feature->f_item_desc; } if (!pointerp(item_desc)) { return item_desc; } return item_desc[0]; } // Called by rooms when chatting, to get feature chats. /** * @ignore */ varargs string get_a_feature_chat(string title, int range, string direc) { class feature_data feature; mixed chat; int night = (WEATHER->query_tod() == "night"); if (!feature) { feature = _features[title]; } if (!feature) { return ""; } if (night) { chat = feature->night_chats; } else { chat = feature->chats; } if (!chat) { return ""; } if (arrayp(chat)) { chat = chat[random(sizeof(chat))]; } if (arrayp(chat)) { // ranged chat if ((range < 0) || (range >= sizeof(chat))) { return ""; } chat = chat[range]; } if (!stringp(chat)) {// Something's wrong. return ""; } if (direc) { chat = replace(chat, ({ "$D", direc })); } return chat; } // Called by rooms when chatting, to get outside chats. /** * @ignore */ string get_an_outside_chat(string types) { class char_map_data item; string *chats = ({ }); int index; mixed *item_chats; for (index = 0; index < sizeof(types); index++) { item = _char_map[types[index..index]]; if (!item) { continue; } item_chats = item->chats; if (!item_chats) { continue; } if (stringp(item_chats)) chats += ({ item_chats }); else if (arrayp(item_chats)) chats += item_chats; } if (!sizeof(chats)) return ""; return chats[random(sizeof(chats))]; } /** * This method is used to add an 'obstacle' to an area map. An obstacle is * effectively an area on the map which cannot be entered, and can be seen * from adjacent locations. Any occurences of $D in the description will * be replaced with the direction(s) in which the obstacle can be seen. * An add_item is automatically performed to allow for consistency. * @param type the ASCII type used on the map to denote the obstacle * @param description the description of the obstacle, as seen from * neighbouring rooms * @param items a string, or array of strings giving the corresponding * add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding item desc * @param room_desc allows a description for the obstacle in the key * @example * add_obstacle("*", "Thick green bushes block your way to the $D.", ({ "bush", "bushes" }), "Thick and green.", "Thick bushes"); * @see add_room_type * @see add_road_type * @see set_external_chats * @see add_item */ varargs void add_obstacle_type(string type, string description, mixed items, mixed item_desc, string room_desc) { if (!type) { throw("Bad type"); } if (!items) { items = ({ }); item_desc = ({ }); } if (!room_desc) { room_desc = "Obstacle"; } _char_map[type] = new(class char_map_data, type: AREA_OBSTACLE, adjacent_desc:description, room_desc : room_desc, items:items, item_desc:item_desc); } /** * This method is used to add a standard skip room type to the terrain map. * Mapped to an ASCII character as used in the ASCII room map. A skipped * room is one in which every nth room in the terrain will be skipped * unless it has something interesting in it. The height is used to * do specific sorts of checks, like if a specific direction is passable or not. * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * @param size the number of rooms in each clump to skip over * @param road_skip if this is 1 then the skip type is a 'road skip', this * will assume the path is one wide and try to follow around curves * @param height the height of the room * @param height_rooms is a mapping containing what rooms to use at different height levels * @example * add_room_type(".", ROOMS + "field"); * @see add_room_type * @see add_road_type * @see add_obstacle_type */ void add_special_type(string type, string base_path, int size, int road_skip, int height, mapping height_rooms) { _char_map[type] = new(class char_map_data, type: AREA_ROOM, path:base_path, room_jump : size, room_jump_matching : road_skip, height : height, height_rooms : height_rooms); } /** * This method is used to add a standard room type to the terrain map, * mapped to an ASCII character as used in the ASCII room map. * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * @example * add_room_type(".", ROOMS + "field"); * @see add_room_type * @see add_road_type * @see add_obstacle_type */ void add_room_type(string type, string base_path) { _char_map[type] = new(class char_map_data, type: AREA_ROOM, path:base_path); } /** * This method is used to add a standard road type to the terrain map, * mapped to an ASCII character as used in the ASCII room map. * A road is a terrain map room which can be seen from adjacent locations. * The handler uses 'room_desc' to add an entry to the room description, * describing where the road goes. 'description' is added to the * descriptions of adjacent locations, to enable the road to be seen. * In 'room_desc' and 'description', $D will be expanded to the relevant * directions. * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * @param room_desc the 'continuation' description, which added to the room description to show where the road leads * @param description the 'adjacent' description, which is seen from adjacent locations. * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding item desc * @example * add_road_type("+", ROOMS + "path", "The dusty path leads $D.", * "There is a dusty path to the $D.", * "path", "Hey! Look! A path!"); * @see add_room_type * @see add_road_type * @see set_external_chats * @see add_item */ varargs void add_road_type(string type, string base_path, string room_desc, string description, mixed items, mixed item_desc) { _char_map[type] = new(class char_map_data, type: AREA_ROAD, adjacent_desc:description, items:items, item_desc:item_desc, room_desc:room_desc, path:base_path); } /** * This method is used to add a world map based file into the terrain. * It handles all the options like a special road type, but queries all the * details off the base room instead. * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * @example * add_world_room_type("+", ROOMS + "path", "The dusty path leads $D.", * "There is a dusty path to the $D.", * "path", "Hey! Look! A path!"); * @see add_room_type * @see add_road_type * @see set_external_chats * @see add_item */ varargs void add_world_room_type(string type, string base_path) { mapping bits; string *tmp; // If the world type doesn't exist see if we can find a more // generic version instead. if(file_size(base_path + ".c") == -1) { tmp = explode(base_path, "_edge"); if(sizeof(tmp) && file_size(tmp[0] + ".c") > 0) { debug_printf("%s doesn't exist, using %s", base_path, tmp[0]); base_path = tmp[0]; } } if (file_size(base_path + ".c") > 0) { bits = base_path->query_adjacent_items(); if (!mapp(bits)) { debug_printf("Incorrect room type %O", type); } else { _char_map[type] = new(class char_map_data, type: AREA_WORLD, path: base_path, adjacent_desc:base_path->query_adjacent_description(), items:keys(bits), item_desc:values(bits), //room_desc:room_desc, room_jump : base_path->query_room_jump_size(), height : base_path->query_default_height(), room_jump_matching : base_path->query_follow_road()); } } else { debug_printf("Unable to add %O since %O doesn't exist.", type, base_path); } } /** * This method is used to add a standard road type to the terrain map, * mapped to an ASCII character as used in the ASCII room map. * A road is a terrain map room which can be seen from adjacent locations. * The handler uses 'room_desc' to add an entry to the room description, * describing where the road goes. 'description' is added to the * descriptions of adjacent locations, to enable the road to be seen. * In 'room_desc' and 'description', $D will be expanded to the relevant * directions. * A skipped * room is one in which every nth room in the terrain will be skipped * unless it has something interesting in it. * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * @param size the number of rooms in each clump to skip over * @param road_skip if this is 1 then the skip type is a 'road skip', this * will assume the path is one wide and try to follow around curves * @param height the height of the room * @param room_desc the 'continuation' description, which added to the room description to show where the road leads * @param description the 'adjacent' description, which is seen from adjacent locations. * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding item desc * @param height_rooms is a mapping containing what rooms to use at different height levels * @example * add_road_type("+", ROOMS + "path", "The dusty path leads $D.", * "There is a dusty path to the $D.", * "path", "Hey! Look! A path!"); * @see add_room_type * @see add_road_type * @see set_external_chats * @see add_item */ varargs void add_road_special_type(string type, string base_path, int size, int road_skip, int height, string room_desc, string description, mixed items, mixed item_desc, mapping height_rooms) { _char_map[type] = new(class char_map_data, type: AREA_ROAD, adjacent_desc:description, items:items, item_desc:item_desc, room_desc:room_desc, path:base_path, room_jump : size, height : height, height_rooms : height_rooms, room_jump_matching : road_skip); } /** * This method is used to add a standard exit to a specified location * in a terrain map. The chief use is to insert exits to standard rooms * or other terrain maps. * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @param direc the direction of the exit (as in add_exit) * @param dest the path of the exit's destination (as in add_exit) * @param type the type of exit (as in add_exit) * @param modifiers an optional array of exit modifiers (as in modify_exit) * @example * add_exit_at(8, 0, "south", PEARWOOD + "stream_entrance", "road", * ({ "move mess", "You wade along the stream bed.\n", * "enter mess", "$N wades in from the north.", * "exit mess", "$N wades off to the $T." })); * * @see add_room_type * @see add_road_type * @see add_obstacle_type * @see add_exit * @see modify_exit */ varargs void add_exit_at(int x, int y, string direc, string dest, string type, mixed modifiers) { string title = sprintf("%d:%d:%d", x, y, 0); if (!dest) return; if (!type) type = "road"; if (!_standard_exits[title]) _standard_exits[title] = ({ }); _standard_exits[title] += ({ ([ "direc":direc, "dest":dest, "type":type, "modifiers":modifiers ]) }); } /** * This method is used to add a standard exit to a specified location * in a terrain map. The chief use is to insert exits to standard rooms * or other terrain maps. * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @param direc the direction of the exit (as in add_exit) * @param dest the path of the exit's destination (as in add_exit) * @param type the type of exit (as in add_exit) * @param modifiers an optional array of exit modifiers (as in modify_exit) * @example * add_exit_at(8, 0, "south", PEARWOOD + "stream_entrance", "road", * ({ "move mess", "You wade along the stream bed.\n", * "enter mess", "$N wades in from the north.", * "exit mess", "$N wades off to the $T." })); * * @see add_room_type * @see add_road_type * @see add_obstacle_type * @see add_exit * @see modify_exit */ varargs void add_z_exit_at(int x, int y, int z, string direc, string dest, string type, mixed modifiers) { string title = sprintf("%d:%d:%d", x, y, z); if (!dest) return; if (!type) type = "road"; if (!_standard_exits[title]) _standard_exits[title] = ({ }); _standard_exits[title] += ({ ([ "direc":direc, "dest":dest, "type":type, "modifiers":modifiers ]) }); } /** * This method is used to limit the number of random descs that are * picked out for each room. * @param new_limit the new desc limit * @see set_random_chat_limit * @see add_random_desc * @see add_random_chat */ void set_random_desc_limit(int new_limit) { _random_desc_limit = new_limit; } /** * This method is used to limit the number of random chats that are * picked out for each room. * @param new_limit the new chat limit * @see set_random_desc_limit * @see add_random_desc * @see add_random_chat */ void set_random_chat_limit(int new_limit) { _random_chat_limit = new_limit; } /** * This method is to add to the list of random descriptions that can be added to * specific room types. A specific random sequence is used so that the randoms * won't vary over time. * @param types the string of ASCII character type(s) that this desc could apply to * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding item desc * @see set_random_desc_limit * @see set_random_chat_limit * @see add_random_chat * @see add_item */ void add_random_desc(string types, string description, mixed items, mixed item_desc) { int index; for (index = 0; index < sizeof(types); index++) { if (!_random_descs[types[index..index]]) _random_descs[types[index..index]] = ({ }); _random_descs[types[index..index]] += ({ ([ "desc": description, "items":items, "item_desc":item_desc ]) }); } } /** * This method is to add to the list of random chats that can be added to * specific room types. A specific random sequence is used so that the randoms * won't vary over time. * @param types the string of ASCII character type(s) that this desc could apply to * @param chats an array of chats to add. Usual add_chat format. * @see set_random_desc_limit * @see set_random_chat_limit * @see add_random_chat */ void add_random_chat(string types, string *chats) { int index; for (index = 0; index < sizeof(types); index++) { if (!_random_chats[types[index..index]]) _random_chats[types[index..index]] = ({ }); _random_chats[types[index..index]] += chats; } } /** * This method is used to setup 'external' chats for a road or obstacle, * which will be added to the room chats of adjacent locations. You * should supply the ASCII character corresponding to the road/obstacle * type, and an array of chat strings (in the same format as supplied to * room_chat) * @param type the ASCII type used on the map to denote the room * @param base_path the full pathname of the source file for the room * set_external_chats("=", * ({ "The stream bubbles merrily on its way.", * "You hear a faint splashing sound from the stream." })); * @see add_road_type * @see add_obstacle_type * @see set_feature_chats * @see room_chat */ void set_external_chats(string type, mixed chats) { if (!_char_map[type]) { return; } ((class char_map_data)_char_map[type])->chats = chats; } /** * This method is called to add a new 'feature' to a terrain map. * Features have no real existance, but add atmosphere to a whole area, * by being visible across a large number of locations. * You define location, size, item desc, and add an array of 'longs' * sorted by distance, plus the usual item descriptions. Any occurrences * of $D in the longs array will be replaced by the direction in which * the feature lies. * @param title the name of the feature * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @param width the width of the feature * @param height the height of the feature * @param range the distance (number of rooms) it can be seen for * @param descs array of descriptions to be shown at different ranges. * These are divided equally along the total range * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * @param night_descs the night description array * @param night_items the descriptions for the items at night * @example * add_feature("northmountain", 3, 0, 5, 1, 7, * ({ "A huge snowy mountain towers over you to the $D.", * "A huge snowy mountain looms to the $D.", * "A fair way off to the $D, you can see a huge snowy mountain.", * "A long way $Dwards, you glimpse the snowy peak of a " * "mountain." }), * "mountain", "From here, it looks quite astoundingly huge.", * ({ "A huge black bulk obscures the sky to the $D.", * "A huge black bulk covers the horizon to the $D.", * "A black lump obscures the horizon to the $D", * "" }), * "The dark black silloette looks like a mountain."); * @see add_road_type * @see add_obstacle_type * @see set_feature_chats * @see modify_feature * @see add_item */ void add_feature(string title, int x, int y, int width, int height, int range, string *descs, mixed items, mixed item_desc, string* night_descs, mixed night_items) { object region; object feature; region = clone_object(TERRAIN_MAP_REGION_RECTANGULAR); region->add_feature_position(x, y, width, height); feature = clone_object(TERRAIN_MAP_FEATURE_BASE); feature->set_region(region); feature->set_max_day_range(range); feature->set_max_night_range(range); _features[title] = new(class feature_data, range:range, descs:descs, f_items:items, f_item_desc:item_desc, f_item_night_desc : night_items, whole_sentance:1, feature_ob : feature, night_descs : night_descs); } /** * This method is called to add a new 'feature' to a terrain map. * Features have no real existance, but add atmosphere to a whole area, * by being visible across a large number of locations. This method is * different to the normal add_feature in that the system will try * and construct a sentance from the features instead of once sentance * per feature. * You define location, size, item desc, and add an array of 'longs' * sorted by distance, plus the usual item descriptions. Any occurrences * of $D in the longs array will be replaced by the direction in which * the feature lies. * @param title the name of the feature * @param x the x coordinate of the room (internal coordinate system) * @param y the y coordinate of the room (internal coordinate system) * @param width the width of the feature * @param height the height of the feature * @param range the distance (number of rooms) it can be seen for * @param descs array of descriptions to be shown at different ranges. * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * These are divided equally along the total range * @example * add_feature_sentance("northmountain", 3, 0, 5, 1, 7, * ({ "a huge snowy mountain towers over you to the $D", * "a huge snowy mountain looms to the $D", * "a fair way off to the $D, you can see a huge snowy mountain", * "a long way $Dwards, you glimpse the snowy peak of a " * "mountain" }), * "mountain", "From here, it looks quite astoundingly huge." ); * @see add_road_type * @see add_obstacle_type * @see set_feature_chats * @see modify_feature * @see add_item */ void add_feature_sentance(string title, int x, int y, int width, int height, int range, string *descs, mixed items, mixed item_desc, string* night_descs, mixed night_items) { object region; object feature; region = clone_object(TERRAIN_MAP_REGION_RECTANGULAR); region->add_feature_position(x, y, width, height); feature = clone_object(TERRAIN_MAP_FEATURE_BASE); feature->set_region(region); feature->set_max_day_range(range); feature->set_max_night_range(range); _features[title] = new(class feature_data, feature_ob : feature, range:range, descs:descs, f_items:items, f_item_desc:item_desc, f_item_night_desc : night_items, whole_sentance:0, night_descs : night_descs); } /** * This method is called to add a new 'feature' to a terrain map. * Features have no real existance, but add atmosphere to a whole area, * by being visible across a large number of locations. * You define location, size, item desc, and add an array of 'longs' * sorted by distance, plus the usual item descriptions. Any occurrences * of $D in the longs array will be replaced by the direction in which * the feature lies. * @param title the name of the feature * @param region the region object to use in calculating the feature * @param range the distance (number of rooms) it can be seen for * @param descs array of descriptions to be shown at different ranges. * These are divided equally along the total range * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * @param whole_sentance use a whole sentance for the descriptions or wrap * them up into fragments * @example * region = clone_object(TERRAIN_MAP_REGION_POLYGON); * region->add_vertex(12, 13); * region->add_vertex(14,15); * region->add_vertex(17,18); * add_feature_region("northmountain", region, 7, * ({ "A huge snowy mountain towers over you to the $D.", * "A huge snowy mountain looms to the $D.", * "A fair way off to the $D, you can see a huge snowy mountain.", * "A long way $Dwards, you glimpse the snowy peak of a " * "mountain." }), * "mountain", "From here, it looks quite astoundingly huge.", * ({ }), 0 ); * @see add_road_type * @see add_obstacle_type * @see set_feature_chats * @see modify_feature * @see add_item */ void add_feature_region(string title, object region, int range, string *descs, mixed items, mixed item_desc, string* night_descs, mixed night_items, int whole_sentance) { object feature; feature = clone_object(TERRAIN_MAP_FEATURE_BASE); feature->set_region(region); feature->set_max_day_range(range); feature->set_max_night_range(range); _features[title] = new(class feature_data, range:range, descs:descs, f_items:items, f_item_desc:item_desc, whole_sentance:whole_sentance, f_item_night_desc : night_items, feature_ob : feature, night_descs : night_descs); } /** * This method is called to add a new 'feature' to a terrain map. * Features have no real existance, but add atmosphere to a whole area, * by being visible across a large number of locations. * You define location, size, item desc, and add an array of 'longs' * sorted by distance, plus the usual item descriptions. Any occurrences * of $D in the longs array will be replaced by the direction in which * the feature lies. * @param title the name of the feature * @param region the region object to use in calculating the feature * @param range the distance (number of rooms) it can be seen for * @param descs array of descriptions to be shown at different ranges. * These are divided equally along the total range * @param items add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * @param whole_sentance use a whole sentance for the descriptions or wrap * them up into fragments * @example * region = clone_object(TERRAIN_MAP_REGION_POLYGON); * region->add_vertex(12, 13); * region->add_vertex(14,15); * region->add_vertex(17,18); * add_feature_region("northmountain", region, 7, * ({ "A huge snowy mountain towers over you to the $D.", * "A huge snowy mountain looms to the $D.", * "A fair way off to the $D, you can see a huge snowy mountain.", * "A long way $Dwards, you glimpse the snowy peak of a " * "mountain." }), * "mountain", "From here, it looks quite astoundingly huge.", * ({ }), 0 ); * @see add_road_type * @see add_obstacle_type * @see set_feature_chats * @see modify_feature * @see add_item */ void add_feature_ob(string title, object feature) { if (_real_coords && _real_coords->xdiff) { feature->set_distance_multiplier(_real_coords->xdiff); } _features[title] = new(class feature_data, f_items : feature->query_items(), f_item_desc : feature->query_day_items(), f_item_night_desc : feature->query_night_items(), feature_ob : feature); } /** * This method is used to modify the descriptions and/or item descs of * an established feature. Changes will be instantly seen in all the * rooms the feature is visible from. * @param title the name of the feature * @param descs array of descriptions to be shown at different ranges. * add_item name(s) (identical to the 'shorts' parameter in add_item) * @param item_desc a string, or array of strings giving the corresponding * These are divided equally along the total range * @see set_feature_chats * @see add_feature * @see add_item */ varargs void modify_feature(string title, string *descs, mixed item_desc) { if (!_features[title]) { return; } if (descs) { _features[title]->descs = descs; } if (item_desc) { _features[title]->f_item_desc = item_desc; } } /** * This method is used to add room chats to locations that can view a * particular feature. $D substitution is carried out on the chats. * @param title the name of the feature * @param chats an array of strings (room chats related to the feature) * @see add_feature * @see modify_feature * @see room_chat */ void set_feature_chats(string title, mixed *chats, mixed* night_chats) { if (!_features[title]) { return; } ((class feature_data)_features[title])->chats = chats; ((class feature_data)_features[title])->night_chats = night_chats; } /** * This method is used to remove a named feature from the terrain map * @param title the name of the feature * @see add_feature */ void remove_feature(string title) { object room; if (!_features[title]) { return; } map_delete(_features, title); if (!_feature_rooms[title]) { return; } _feature_rooms[title] -= ({ 0 }); foreach(room in _feature_rooms[title]) { room->remove_feature(title); } map_delete(_feature_rooms, title); } /** * This method is used to add a signpost into a specified location in a * terrain_map. It is identical to the /std/room/ add_sign, except for the x and y coordinates which specify where the sign should go. * @param title the name of the feature * @param x the x coordinate of the center point * @param y the y coordinate of the center point * @example * add_sign(4, 3, "A wooden signpost.\n", * "\n-----------------\n" * "| Ladder: East |\n" * "| Bridge: South |\n" * "-----------------", * "signpost", ({ "signpost", "sign" }), "common"); * * @see add_sign */ void add_sign(int x, int y, string sign_long, mixed sign_read_mess, string sign_short, mixed sign_name, string sign_language) { string title = sprintf("%d:%d", x, y); _signposts[title] = new(class sign_data, long:sign_long, read_mess:sign_read_mess, short:sign_short, name:sign_name, language:sign_language); } /** * This method is used to map ASCII characters used in the zone map onto * one or more actual room zones. * @param type the ASCII character used in the zone map * @param zones a zone name or an array of zone names * @example * add_zones("*", ({ "zone2", "zone1" })); */ void add_zones(string type, string *zone_names) { _zone_types[type] = zone_names; } // Dests all the rooms, and reloads all the maps. /** * @ignore */ void reset_handler() { mixed *rooms; if (arrayp(_room_map)) { foreach(rooms in _room_map) { rooms->dest_me(); } } _room_map = 0; _area_map = 0; setup_area_map(); } // Load up the zone map. Based on the map_file name. /** * @ignore */ void load_zone_map() { string map_path = query_map_file() + ".zones"; int index = 1; string line; int width; int height; _zone_map = ({ }); while (line = read_file(map_path, index++, 1)) { if (width < sizeof(line)) width = sizeof(line); _zone_map += ({ line }); } height = index - 1; if ((width != _width) || (height != _height)) _zone_map = ({ }); } // Load up the area map. Based on the map_file name. /** * @ignore */ void load_area_map() { string map_path = query_map_file() + ".map"; int index = 1; int height; string line; _area_map = ({ }); while (line = read_file(map_path, index++, 1)) { if (sizeof(line)) { line = line[0..<2]; if (_width < sizeof(line)) { _width = sizeof(line); } //if (sizeof(line) < _width) //continue; _area_map = ({ line }) + _area_map; height++; } } _height = height; if (!height) { printf("Unable to open %O\n", map_path); } } /** * @ignore * Handles the cloning of a room when it is specified as: * path/name:x:y */ object create_virtual_object(string x_str, string y_str, string z_str) { int x = to_int(x_str); int y = to_int(y_str); int z = to_int(z_str); if (!stringp(x_str) || !stringp(y_str) || !stringp(z_str)) { return 0; } return query_room(x, y, z); } /** * This method finds the room at the specific real coordinate. * @param x the x location * @param y the y location * @return the room, or 0 on failure */ string find_room_at_real_coord(int x, int y, int z) { class char_map_data room_map; class coord our; our = query_terrain_from_real_coord(x, y, z); if (!our) { return 0; } room_map = query_char_map(our->x, our->y); if (!room_map) { debug_printf("Unable to load room at %d %d (%d %d)\n", our->x, our->y, x, y); return 0; } if (room_map->type == AREA_OBSTACLE) { return 0; } return sprintf("%s:%d:%d:%d", base_name(this_object()), our->x, our->y, our->z); } /* find_room_at_real_coord() */ /** * This method returns the distance that is moved if you go in the * specified direction. * @return the distance */ int query_direction_distance(string dir) { switch (_direcs[dir][3]) { case X_DIFF : return _real_coords->xdiff; case Y_DIFF : return _real_coords->ydiff; case XY_DIFF : return _real_coords->xydiff; } return 0; } /** @ignore yes */ void dest_me() { class feature_data womble; string name; foreach (name, womble in _features) { if (womble->feature_ob) { womble->feature_ob->dest_me(); } } destruct(this_object()); }