/* -*- LPC -*- */ /* * $Locker: pinkfish $ * $Id: area.c,v 1.3 2002/02/28 23:26:06 rhinehold Exp pinkfish $ * * */ /* ** area.c - base object for a terrain-like set of rooms. */ #include <dirs.h> #include <image.h> inherit "/std/room"; mapping direction_bits = ([ "north": 1, "northeast": 2, "east": 4, "southeast": 8, "south": 16, "southwest": 32, "west": 64, "northwest": 128, "up": 256, "down": 512, ]); class AREA_INFO { string base; int *origin; // Origin of area; room centers are 2*size offset from this int *size; // Size of each room mixed *bbox; // Keeps track of bounding box of area (absolute) mixed *vertices; // Outline of area (absolute) class IMAGE_INFO height; // Height map of area class IMAGE_INFO exits; // Map of exits (for twisty little passages :) } /* AREA_INFO */ private int dbg_lvl = 0; private string debugger = "jeremy"; private string _handler; private class AREA_INFO *areas = ({ }); private mapping room_cache = ([ ]); private mapping area_cache = ([ ]); /** * This method sets the handler of the topographic region. * @param handler the handler to set */ void set_handler(string handler) { _handler = handler; } /** * This method returns the handler of the topographic region. * @return the handler of the whole region */ string query_handler() { return _handler; } /* query_handler() */ /** * This method sets the creator to whom debugging messages are sent. * @param d name of creator */ void set_debugger(string s) { debugger = s; } /** * This method returns the name of the creator to whom debugging messages * are sent. * @return name of creator */ string query_debugger() { return debugger; } /** * This method sets the level of debugging messages printed. * @param l debugging level (0 is off) */ void set_debug_level(int l) { dbg_lvl = l; } /** * This method returns the current debugging level. * @return current debug level (0 is off) */ int query_debug_level() { return dbg_lvl; } /** * @ignore * For debugging only */ mixed query(string s) { return fetch_variable(s); } void set_base(int i, string s) { areas[i]->base = s; } string query_base(int i) { return areas[i]->base; } void set_origin(int i, int *o) { areas[i]->origin = copy(o); } int *query_origin(int i) { return copy(areas[i]->origin); } void create() { do_setup++; ::create(); do_setup--; // Keeps us from being unloaded (one of the drawbacks of making the // handler a room; I wonder if that was a smart move...). set_keep_room_loaded(1); if ( !do_setup ) { this_object()->setup(); this_object()->reset(); } } /* create() */ // Quantize coord to origin in area a int *quantize_to_origin(int *coord_r, int a) { int size2, i, *coord_q; coord_q = allocate(3); for (i = 0; i < 3; i++) { size2 = areas[a]->size[i] * 2; if (coord_r[i] < areas[a]->origin[i]) { coord_q[i] = coord_r[i] - areas[a]->size[i]; } else { coord_q[i] = coord_r[i] + areas[a]->size[i]; } coord_q[i] = ((coord_q[i] - areas[a]->origin[i]) / size2) * size2 + areas[a]->origin[i]; } return coord_q; } /* quantize_to_origin() */ void set_size(int i, mixed s) { if (intp(s)) { areas[i]->size = ({ s, s, s }); } else if (arrayp(s) && (sizeof(s) == 3)) { areas[i]->size = copy(s); } else { error("Illegal size; must be int or 3-element array.\n"); } } /* set_size() */ //int *query_size(int i) { return copy(areas[i]->size); } varargs void set_height_map(int i, string map_file, int rle) { int *nw; areas[i]->height = IMAGE_HANDLER->load_image(map_file, rle); if (arrayp(areas[i]->bbox)) { // Set up coordinate of NW corner nw = copy(areas[i]->bbox[0]); nw[0] = areas[i]->bbox[1][0] - 1; nw[1] += 1; nw[2] += 1; areas[i]->height->nw_coord = quantize_to_origin(nw, i); } } varargs void set_exits_map(int i, string map_file, int rle) { int *nw; areas[i]->exits = IMAGE_HANDLER->load_image(map_file, rle); if (arrayp(areas[i]->bbox)) { // Set up coordinate of NW corner nw = copy(areas[i]->bbox[0]); nw[0] = areas[i]->bbox[1][0] - 1; nw[1] += 1; nw[2] += 1; areas[i]->exits->nw_coord = quantize_to_origin(nw, i); } } int new_area() { areas += ({ new(class AREA_INFO) }); } /* new_area() */ // Note: this must be done after the origin is set void set_vertices(int a, mixed *vs) { int v, i; if (!arrayp(areas[a]->origin)) { error("Origin unset - setting to (0,0,0).\n"); areas[a]->origin = ({ 0, 0, 0 }); } areas[a]->vertices = copy(vs); // Initialize bounding box areas[a]->bbox = ({ copy(vs[0]), copy(vs[0]) }); for (i = 0; i < 3; i++) { areas[a]->bbox[0][i] += areas[a]->origin[i]; areas[a]->bbox[1][i] += areas[a]->origin[i]; } // Change to absolute coordinates for (v = 0; v < sizeof(areas[a]->vertices); v++) { for (i = 0; i < 3; i++) { areas[a]->vertices[v][i] += areas[a]->origin[i]; if (areas[a]->vertices[v][i] < areas[a]->bbox[0][i]) { areas[a]->bbox[0][i] = areas[a]->vertices[v][i]; } else if (areas[a]->vertices[v][i] > areas[a]->bbox[1][i]) { areas[a]->bbox[1][i] = areas[a]->vertices[v][i]; } } } } /* set_vertices() */ int in_bbox(int *coord, mixed *bbox) { if ((coord[0] < bbox[0][0]) || (coord[0] > bbox[1][0])) { return 0; } if ((coord[1] < bbox[0][1]) || (coord[1] > bbox[1][1])) { return 0; } if ((coord[2] < bbox[0][2]) || (coord[2] > bbox[1][2])) { return 0; } return 1; } /* in_bbox() */ int in_area(int a, int *v) { // This counts how many times a line from v straight north crosses // the edges. If it's odd, we're in the area. // REF: Foley, J.D. and A. Van Dam, Fundamentals of Interactive Computer // Graphics, Addison-Wesley, 1982, pp 457-458. // This may be more complicated than it has to be. I found a much simpler // implementation in C, but I haven't verified it yet. int crossings, i, i0, i1, de, int_n, below_vertex; mixed *vs; if (!in_bbox(v, areas[a]->bbox)) { //printf("Outside (bbox)...\n"); return 0; } // This looks complicated, but most of the time it shouldn't get past // the first four tests. vs = allocate(2); for (i = 0; i < sizeof(areas[a]->vertices); i++) { below_vertex = 0; vs[0] = areas[a]->vertices[i]; if (i < sizeof(areas[a]->vertices)-1) { vs[1] = areas[a]->vertices[i+1]; } else { vs[1] = areas[a]->vertices[0]; } if ((v[1] < vs[0][1]) && (v[1] < vs[1][1])) { //printf("Outside (west)...\n"); continue; } if ((v[1] > vs[0][1]) && (v[1] > vs[1][1])) { //printf("Outside (east)...\n"); continue; } if ((v[0] > vs[0][0]) && (v[0] > vs[1][0])) { //printf("Outside (north)...\n"); continue; } if ((v[0] < vs[0][0]) && (v[0] < vs[1][0])) { // We're below the line segment. if (v[1] != vs[0][1]) { // We're not below the first vertex... if (v[1] != vs[1][1]) { // We're not below the second vertex, so we cross the line. crossings++; } // Don't count hitting the vertex on the second point (see below) continue; } else { // Need to check whether to count the vertex as a crossing. below_vertex = 1; } } // Okay, now we get into some fuzzier cases. We're somewhere in the // bounding box of the line segment, or we hit a vertex. if (vs[0][1] == vs[1][1]) { // Don't count vertical lines unless we're sitting right on it. if (!below_vertex) { return 1; } continue; } if ((v[0] == vs[0][0]) && (v[1] == vs[0][1])) { // We're on a vertex - we're "in". return 1; } if ((vs[0][0] == vs[1][0]) && !below_vertex) { // We're on a horizontal line - we're "in". return 1; } // Vertices are tough...we need to find if it's concave or convex if (v[1] == vs[1][1]) { // Don't count vertex hit on second point (or else it'll get // counted twice). continue; } if (v[1] == vs[0][1]) { if (v[0] > vs[0][0]) { // We're above the first vertex. No intersection. continue; } // Find closest bend on one side... i0 = i; while (areas[a]->vertices[i0][1] == v[1]) { i0--; if (i0 < 0) i0 = sizeof(areas[a]->vertices) - 1; } // Find closest bend on the other side... i1 = i; while (areas[a]->vertices[i1][1] == v[1]) { i1++; if (i1 >= sizeof(areas[a]->vertices)) i1 = 0; } // This counts as at least one hit. crossings++; if (((areas[a]->vertices[i0][1] > v[1]) && (areas[a]->vertices[i1][1] > v[1])) || ((areas[a]->vertices[i0][1] < v[1]) && (areas[a]->vertices[i1][1] < v[1]))) { // Concave/convex count as 2 crossings. crossings++; } continue; } // Nothing left to do but find the intersection de = vs[1][1] - vs[0][1]; int_n = ceil(to_float(vs[1][0] - vs[0][0])*(v[1] - vs[0][1])/de + vs[0][0] + 0.5); if (int_n > v[0]) { crossings++; } } // LSB indicates if it's odd. return (crossings & 1); } /* in_area() */ object clone_room(int a, int *coord_q, string coord_s) { int i, j, e, bit; string dir; object room; room = clone_object(areas[a]->base); room_cache[coord_s] = room; area_cache[coord_s] = a; room->set_room_size(areas[a]->size); // TODO: add height map stuff room->set_co_ord(coord_q); if (areas[a]->exits) { // Add exits to room according to map i = (areas[a]->origin[0] - coord_q[0])/(2*areas[a]->size[0]); j = (coord_q[1] - areas[a]->origin[1])/(2*areas[a]->size[1]); if ((i < areas[a]->exits->size[0]) && (j < areas[a]->exits->size[1]) && (i >= 0) && (j >= 0)) { e = areas[a]->exits->image[i][j]; foreach (dir, bit in direction_bits) { if (e & bit) { // TODO: should put a field for the exit type in the class... room->add_exit(dir, "", "path"); } } // This is a bit of a hack, but it's easy. room->add_property("image_cell", ({ i, j })); } } // Other room initialization TBD. return room; } object find_room_at_coord(int *coord) { int a, ac, *coord_q; string coord_s; object room; // Check to see if it's in the cache as-is. coord_s = sprintf("%d:%d:%d", coord[0], coord[1], coord[2]); if (objectp(room = room_cache[coord_s])) { return room; } if (!undefinedp(ac = area_cache[coord_s])) { // There used to be a room here. return clone_room(ac, coord, coord_s); } for (a = 0; a < sizeof(areas); a++) { // Quantize coordinates to room centers for this area. coord_q = quantize_to_origin(coord, a); // Check the cache again. coord_s = sprintf("%d:%d:%d", coord_q[0], coord_q[1], coord_q[2]); if (objectp(room = room_cache[coord_s])) { return room; } if (!undefinedp(ac = area_cache[coord_s])) { // There used to be a room here. return clone_room(ac, coord_q, coord_s); } if (!in_area(a, coord_q)) { continue; } return clone_room(a, coord_q, coord_s); } return 0; } /* find_room_at_coord() */ object find_room_at_exit(object r, int *d, string direc) { int a, ac, i, *coord, *coord_r, *coord_q; string coord_s; object room; mixed size_r; if (dbg_lvl > 0) { tell_creator(debugger, "%s::find_room_at_exit()\n" " r: %O\n" " d: %O\n" " direc: %O\n", file_name(this_object()), r, d, direc); } if (!arrayp(d)) { return 0; } coord = r->query_co_ord(); // Some rooms (like milestones) have "fake" sizes. size_r = r->query_actual_room_size(); if (!arrayp(size_r)) { size_r = r->query_room_size(); if (!arrayp(size_r)) { // Must be a single int for all 3 dimensions. size_r = ({ size_r, size_r, size_r }); } } coord_r = allocate(3); for (a = 0; a < sizeof(areas); a++) { for (i = 0; i < 3; i++) { coord_r[i] = coord[i] + (size_r[i] + areas[a]->size[i]) * d[i]; } // Use the drivers virtual mapping stuff to do the cacheing for us. coord_s = sprintf("%d:%d:%d", coord_r[0], coord_r[1], coord_r[2]); return load_object(_handler + ":" + coord_s); #if 0 // Check to see if it is (or has been) in the cache as-is. if (objectp(room = room_cache[coord_s])) { if (dbg_lvl > 0) { tell_creator(debugger, "%s::find_room_at_exit()\n" " found room %O cached at %s...\n", file_name(this_object()), room, coord_s); } return room; } if (!undefinedp(ac = area_cache[coord_s])) { // There used to be a room here. if (dbg_lvl > 0) { tell_creator(debugger, "%s::find_room_at_exit()\n" " found area %O cached at %s...\n", file_name(this_object()), ac, coord_s); } return clone_room(ac, coord_r, coord_s); } coord_q = quantize_to_origin(coord_r, a); // Check the cache again. coord_s = sprintf("%d:%d:%d", coord_q[0], coord_q[1], coord_q[2]); if (objectp(room = room_cache[coord_s])) { return room; } if (!undefinedp(ac = area_cache[coord_s])) { // There used to be a room here. return clone_room(ac, coord_q, coord_s); } if (!in_area(a, coord_q)) { continue; } return clone_room(a, coord_q, coord_s); #endif } return 0; } /* find_room_at_exit() */ // This is a testing convenience function int goto_room_at_coord(int *coord) { return this_player()->move_with_look(find_room_at_coord(coord)); } /* goto_room_at_coord() */ int goto_room_at_exit(int *d, string direc) { return this_player()->move_with_look(find_room_at_exit( environment(this_player()), d, direc)); } void print_images(int a) { if (areas[a]->height) { printf("-------- Height:\n"); IMAGE_HANDLER->print_image(areas[a]->height); } if (areas[a]->exits) { printf("-------- Exits:\n"); IMAGE_HANDLER->print_image(areas[a]->exits); } } void print_map(int a) { int s, e; string l = ""; class IMAGE_INFO exits; if (areas[a]->exits) { exits = areas[a]->exits; for (s = 0; s < exits->size[1]; s++) { for (e = 0; e < exits->size[0]; e++) { l += (exits->image[s][e] & 128)?"\\":" "; l += (exits->image[s][e] & 1) ?"|":" "; l += (exits->image[s][e] & 2) ?"/":" "; } l += "\n"; for (e = 0; e < exits->size[0]; e++) { l += (exits->image[s][e] & 64) ?"-":" "; l += "*"; l += (exits->image[s][e] & 4) ?"-":" "; } l += "\n"; for (e = 0; e < exits->size[0]; e++) { l += (exits->image[s][e] & 32) ?"/":" "; l += (exits->image[s][e] & 16) ?"|":" "; l += (exits->image[s][e] & 8) ?"\\":" "; } l += "\n"; } write(l); } }