/* File: borg2.c */ /* Purpose: Low level dungeon mapping skills -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "borg1.h" #include "borg2.h" /* * This file helps the Borg understand mapping the dungeon. * * Currently, this includes general routines involving dungeon grids, * including calculating "flow" values from place to place, determining * "line of sight", plus "field of view" and "torch-lit grids", setting * the target to a given location, and extracting the optimal direction * for "movement" from place to place. * * Note that the dungeon is assumed smaller than 256 by 256. * * This file also supplies the (compiled out) support for "automatic * room extraction". This code will automatically group regions of * the dungeon into rooms, and do the "flow" navigation on those rooms * instead of on grids. Often, this takes less space, and is faster, * howver, it is more complicated, and does not allow "specialized" * flow calculations that penalize grids by variable amounts. */ #ifdef BORG_ROOMS /* * Hack -- access the "set" of rooms containing a given point * * We should probably build a "list" of "used rooms", where the "tail" * of that list is all the "crossed rooms", and also keep a pointer to * that tail for "fast" access to the "crossed rooms" set. * * This function is necessary because some grids (though not many) * can be contained in multiple rooms, because we allow "crossing" * rooms. The Borg uses crossing rooms not only for actual "cross" * and "overlap" rooms in the dungeon, but also weird double width * corridors, the corners of "ragged edge" rooms, and various regions * in the town. Actually, the worst "offenders" come from the town... * * Note that this function should be valid even if rooms are destroyed * inside a single "search" through the room list. This is because * only the "first" call to this function checks the "room" field. */ auto_room *room(bool go, int gy, int gx) { static int x = 0, y = 0, i = 0; /* We just got a new grid */ if (go) { auto_grid *ag = &auto_grids[gy][gx]; /* Default to no rooms */ i = AUTO_ROOMS; /* Paranoia -- no rooms */ if (!ag->room) return (NULL); /* Efficiency -- Single room */ if (ag->room < AUTO_ROOMS) return (&auto_rooms[ag->room]); /* Scan through multiple rooms */ x = gx; y = gy; i = 0; } /* Look for a room */ for (i++; i < auto_room_max; i++) { /* Access the room */ auto_room *ar = &auto_rooms[i]; /* Skip "free" rooms */ if (ar->free) continue; /* If the room contains it, use it */ if ((x >= ar->x1) && (x <= ar->x2) && (y >= ar->y1) && (y <= ar->y2)) { return (ar); } } /* Default */ return (NULL); } /* * Mega-Hack -- clean up the "free room" list * First, destroy some of the "fake" rooms * Then, attempt to compress the list */ bool borg_free_room_update(void) { int x, y, n = 0; auto_grid *ag; auto_room *ar; /* Scan the dungeon */ for (y = 0; y < AUTO_MAX_Y; y++) { for (x = 0; x < AUTO_MAX_X; x++) { /* Extract the "grid" */ ag = &auto_grids[y][x]; /* Skip "known" grids */ if (ag->feat != FEAT_NONE) continue; /* Skip "viewable" grids */ if (ag->info & BORG_VIEW) continue; /* Only process "roomed" grids */ if (!ag->room) continue; /* Paranoia -- skip cross rooms */ if (ag->room >= AUTO_ROOMS) continue; /* Get the (solitary) room */ ar = &auto_rooms[ag->room]; /* Paranoia -- Skip "big" rooms */ if ((ar->x1 < x) || (ar->x2 > x)) continue; if ((ar->y1 < y) || (ar->y2 > y)) continue; /* That room is now "gone" */ ar->when = 0L; ar->x1 = ar->x2 = 0; ar->y1 = ar->y2 = 0; /* Add it to the "free list" */ auto_rooms[ag->room].free = auto_rooms[0].free; /* Drop the room into the "free list" */ auto_rooms[0].free = ag->room; /* No room here */ ag->room = 0; /* Count changes */ n++; } } /* Nothing done */ if (!n) return (FALSE); /* Message */ borg_note(format("# Destroyed %d fake rooms.", n)); /* Rooms destroyed */ return (TRUE); } /* * Mega-Hack -- purge the "free room" list * Could be a little more "clever", I suppose * For example, first nuke all rooms not in view */ static bool borg_free_room_purge(void) { int x, y, n; /* Purge every room */ for (n = 1; n < auto_room_max; n++) { auto_room *ar = &auto_rooms[n]; /* That room is now "gone" */ ar->when = 0L; ar->x1 = ar->x2 = 0; ar->y1 = ar->y2 = 0; /* Reset the "free list" */ ar->free = n + 1; } /* Reset the free list */ auto_room_head->free = 1; /* Maximum room index */ auto_room_max = 1; /* Scan the dungeon */ for (y = 0; y < AUTO_MAX_Y; y++) { for (x = 0; x < AUTO_MAX_X; x++) { /* Extract the "grid" */ auto_grid *ag = &auto_grids[y][x]; /* No room here */ ag->room = 0; } } /* Message */ borg_note(format("# Purged all %d rooms.", n)); /* Rooms destroyed */ return (TRUE); } /* * Grab a room from the free list and return it */ auto_room *borg_free_room(void) { int i; auto_room *ar; /* Running out of free rooms */ if (auto_room_head->free == auto_room_tail->self) { /* Message */ borg_note("# Updating room list!"); /* Try to free some rooms */ if (!borg_free_room_update()) { /* Oops */ borg_note("# Purging room list!"); /* Try to free some rooms */ if (!borg_free_room_purge()) { /* Oops */ borg_oops("broken rooms"); /* Hack -- Attempt to prevent core dumps */ return (&auto_rooms[1]); } } } /* Acquire a "free" room */ i = auto_room_head->free; /* Access the new room */ ar = &auto_rooms[i]; /* Remove the room from the free list */ auto_room_head->free = ar->free; /* Take note of new "maximum" room index */ if (auto_room_max < i + 1) auto_room_max = i + 1; /* The new room is not free */ ar->free = 0; /* Paranoia */ ar->when = 0L; ar->x1 = ar->x1 = 0; ar->y1 = ar->y2 = 0; /* Return the room */ return (ar); } /* * Determine if the given grid is a "snake" grid * A "snake" is a "normal" section of corridor, with no * bends or intersections. Only the "center" counts. * Note that a "1x1" grid (or "diagonal" grid) is a "snake". * A "snake" grid cannot touch "unknown" grids. */ static bool borg_build_snake(int y, int x) { auto_grid *ag; /* Access the center */ ag = &auto_grids[y][x]; /* Central grid must be known */ if (ag->feat == FEAT_NONE) return (FALSE); /* Central grid must be a non wall/door */ if (!borg_cave_floor_bold(y, x)) return (FALSE); /* South/North blockage induces a snake */ if (!borg_cave_floor_bold(y+1, x) && !borg_cave_floor_bold(y-1, x)) return (TRUE); /* East/West blockage induces a snake */ if (!borg_cave_floor_bold(y, x+1) && !borg_cave_floor_bold(y, x-1)) return (TRUE); /* No good */ return (FALSE); } /* * Determine if the given "box" in the world is fully made of "floor" * Currently, we refuse to accept "unknown" grids as "floor" grids */ static bool borg_build_room_floor(int y1, int x1, int y2, int x2) { int x, y; /* Check for "floors" */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { auto_room *ar; /* Access that grid */ auto_grid *ag = &auto_grids[y][x]; /* Refuse to accept walls/doors */ if (!borg_cave_floor_grid(ag)) return (FALSE); /* Refuse to accept unknown grids */ if (ag->feat == FEAT_NONE) return (FALSE); /* Mega-Hack -- Refuse to accept stores */ if ((ag->feat >= FEAT_SHOP_HEAD) && (ag->feat <= FEAT_SHOP_TAIL)) return (FALSE); #ifndef CROSS_ROOMS /* Hack -- Do not "cross" other (large) rooms */ for (ar = room(1, y, x); ar; ar = room(0, 0, 0)) { if (ar->x1 != ar->x2) return (FALSE); if (ar->y1 != ar->y2) return (FALSE); } #endif } } /* Must be okay */ return (TRUE); } /* * Determine if the given "box" in the world is fully "known" */ static bool borg_build_room_known(int y1, int x1, int y2, int x2) { int x, y; /* Check for "unknown" grids */ for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { /* Access that grid */ auto_grid *ag = &auto_grids[y][x]; /* Refuse to accept unknown grids */ if (ag->feat == FEAT_NONE) return (FALSE); } } /* Must be okay */ return (TRUE); } /* * Attempt to build a "bigger" room for the given location * Return TRUE if a "new" room was created, else FALSE * * Possibly problematic rooms: * * ######### * ##.....## Currently this room is parsed as two * #.......# large overlapping rectangles. We should * #.......# probably consider finding the intersection * #.......# of those rooms, and subtracting out the rest * ##.....## resulting in four small and one large room. * ######### * * Note that the "room builder" will automatically find the largest * rectangle, and will automatically "absorb" enclosed rooms. Now * all we need to do is "notice" when we are "crossing" another room. * As long as we do not cross TWO rooms, we should be fine (?). But * observe the following room: * * ######### * ###...### Assume that (somehow) the two "horizontal" rooms * ###...### are constructed first, and then the "vertical" * #.......# room is discovered. Now the room crosses TWO * #.......# other rooms, and we would like to "extract" * ###...### from this union all NINE resulting rectangles... * ###...### * #.......# Note that crossing corridors do NOT have this * #.......# problem, except when "new holes" are made in * ###...### the middle of existing corridors. That is * ###...### something we might consider noticing. * ######### * * Another question to ask is whether we would rather have: * (1) rooms that cross each other * (2) no crossing rooms, but some "touching" rooms * (3) no crossing rooms and no touching rooms * * Note that #2 seems to allow for the "nicest" treatment of corridors. * * Note that a room is NEVER built including "unknown" grids. * * Also note that corridors are handled by a special routine, and rooms * are only constructed out of at least a 2x2 block of grids. Corridors * are never allowed to overlap any other rooms. * * See the "CROSS_ROOM" compile-time define which determine whether the * Borg is allowed to generate "crossing" rooms. This option allows for * "better" room generation, but at the cost of efficiency. */ bool borg_build_room(int y, int x) { uint i, j; int x1, y1, x2, y2; auto_grid *ag; auto_room *ar; bool change = FALSE; /* Attempt to expand a 3x3 room */ if (borg_build_room_floor(y-1, x-1, y+1, x+1)) { x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y + 1; } /* Or attempt to expand a 3x2 room (south) */ else if (borg_build_room_floor(y, x-1, y+1, x+1)) { x1 = x - 1; y1 = y; x2 = x + 1; y2 = y + 1; } /* Or attempt to expand a 3x2 room (north) */ else if (borg_build_room_floor(y-1, x-1, y, x+1)) { x1 = x - 1; y1 = y - 1; x2 = x + 1; y2 = y; } /* Or attempt to expand a 2x3 room (east) */ else if (borg_build_room_floor(y-1, x, y+1, x+1)) { x1 = x; y1 = y - 1; x2 = x + 1; y2 = y + 1; } /* Or attempt to expand a 2x3 room (west) */ else if (borg_build_room_floor(y-1, x-1, y+1, x)) { x1 = x - 1; y1 = y - 1; x2 = x; y2 = y + 1; } /* Or attempt to expand a 2x2 room (south east) */ else if (borg_build_room_floor(y, x, y+1, x+1)) { x1 = x; y1 = y; x2 = x + 1; y2 = y + 1; } /* Or attempt to expand a 2x2 room (south west) */ else if (borg_build_room_floor(y, x-1, y+1, x)) { x1 = x - 1; y1 = y; x2 = x; y2 = y + 1; } /* Or attempt to expand a 2x2 room (north east) */ else if (borg_build_room_floor(y-1, x, y, x+1)) { x1 = x; y1 = y - 1; x2 = x + 1; y2 = y; } /* Or attempt to expand a 2x2 room (north west) */ else if (borg_build_room_floor(y-1, x-1, y, x)) { x1 = x - 1; y1 = y - 1; x2 = x; y2 = y; } /* Hack -- only "snake" grids can grow corridors */ else if (!borg_build_snake(y, x)) { x1 = x; y1 = y; x2 = x; y2 = y; } /* Or attempt to extend a corridor (south) */ else if (borg_build_snake(y+1, x)) { x1 = x; y1 = y; x2 = x; y2 = y + 1; } /* Or attempt to extend a corridor (north) */ else if (borg_build_snake(y-1, x)) { x1 = x; y1 = y - 1; x2 = x; y2 = y; } /* Or attempt to extend a corridor (east) */ else if (borg_build_snake(y, x+1)) { x1 = x; y1 = y; x2 = x + 1; y2 = y; } /* Or attempt to extend a corridor (west) */ else if (borg_build_snake(y, x-1)) { x1 = x - 1; y1 = y; x2 = x; y2 = y; } /* Default to 1x1 grid */ else { x1 = x; y1 = y; x2 = x; y2 = y; } /* Hack -- Single grid (1x1) rooms are boring */ if ((x1 == x2) && (y1 == y2)) return (FALSE); /* Expand a north/south corridor */ if (x1 == x2) { /* Grow south/north */ while (borg_build_snake(y2+1, x)) y2++; while (borg_build_snake(y1-1, x)) y1--; } /* Expand a east/west corridor */ else if (y1 == y2) { /* Grow east/west */ while (borg_build_snake(y, x2+1)) x2++; while (borg_build_snake(y, x1-1)) x1--; } /* Expand a rectangle -- try south/north first */ else if (rand_int(100) < 50) { /* Grow south/north */ while (borg_build_room_floor(y2+1, x1, y2+1, x2)) y2++; while (borg_build_room_floor(y1-1, x1, y1-1, x2)) y1--; /* Grow east/west */ while (borg_build_room_floor(y1, x2+1, y2, x2+1)) x2++; while (borg_build_room_floor(y1, x1-1, y2, x1-1)) x1--; } /* Expand a rectangle -- try east/west first */ else { /* Grow east/west */ while (borg_build_room_floor(y1, x2+1, y2, x2+1)) x2++; while (borg_build_room_floor(y1, x1-1, y2, x1-1)) x1--; /* Grow south/north */ while (borg_build_room_floor(y2+1, x1, y2+1, x2)) y2++; while (borg_build_room_floor(y1-1, x1, y1-1, x2)) y1--; } /* Hack -- refuse to build rooms touching unknowns */ if (!borg_build_room_known(y2+1, x1-1, y2+1, x2+1)) return (FALSE); if (!borg_build_room_known(y1-1, x1-1, y1-1, x2+1)) return (FALSE); if (!borg_build_room_known(y1-1, x2+1, y2+1, x2+1)) return (FALSE); if (!borg_build_room_known(y1-1, x1-1, y2+1, x1-1)) return (FALSE); /* Make sure this room does not exist and is not contained */ for (ar = room(1, y, x); ar; ar = room(0, 0, 0)) { /* Never make a room "inside" another room */ if ((ar->x1 <= x1) && (x2 <= ar->x2) && (ar->y1 <= y1) && (y2 <= ar->y2)) { /* The room already exists */ return (FALSE); } } /* Message */ borg_note(format("# Room (%d,%d to %d,%d)", x1, y1, x2, y2)); /* Access a free room */ ar = borg_free_room(); /* Initialize the new room */ /* ar->x = ar->x1 = x1; ar->x2 = x2;*/ /* ar->y = ar->y1 = y1; ar->y2 = y2;*/ ar->x1 = x1; ar->x2 = x2; ar->y1 = y1; ar->y2 = y2; /* Save the room index */ i = ar->self; /* Absorb old rooms */ for (j = 1; j < auto_room_max; j++) { /* Skip the "current" room! */ if (i == j) continue; /* Get the room */ ar = &auto_rooms[j]; /* Skip "free" rooms */ if (ar->free) continue; /* Skip non-contained rooms */ if ((ar->x1 < x1) || (ar->y1 < y1)) continue; if ((x2 < ar->x2) || (y2 < ar->y2)) continue; /* Scan the "contained" room */ for (y = ar->y1; y <= ar->y2; y++) { for (x = ar->x1; x <= ar->x2; x++) { /* Get the "contained" grid */ ag = &auto_grids[y][x]; /* Normal grids "lose" their parents. */ if (ag->room < AUTO_ROOMS) ag->room = 0; /* Cross-rooms lose one parent */ if (ag->room > AUTO_ROOMS) ag->room--; } } /* That room is now "gone" */ ar->when = 0L; /* ar->x1 = ar->x2 = ar->x = 0; */ /* ar->y1 = ar->y2 = ar->y = 0; */ ar->x1 = ar->x2 = 0; ar->y1 = ar->y2 = 0; /* Add it to the "free list" */ auto_rooms[j].free = auto_rooms[0].free; auto_rooms[0].free = j; } /* Access the new room */ ar = &auto_rooms[i]; /* Scan the grids contained in the new room */ for (y = ar->y1; y <= ar->y2; y++) { for (x = ar->x1; x <= ar->x2; x++) { /* Get the "contained" grid */ ag = &auto_grids[y][x]; /* Steal "absorbed" grids */ if (ag->room == AUTO_ROOMS) ag->room = ar->self; /* Steal "fresh" grids */ if (ag->room == 0) ag->room = ar->self; /* Skip grids owned by this room */ if (ag->room == i) continue; /* Normal grids become "cross-grids" (one parent) */ if (ag->room < AUTO_ROOMS) ag->room = AUTO_ROOMS + 1; /* All cross-grids now have another parent */ ag->room++; } } /* Changed */ change = TRUE; /* Changed */ return (change); } /* * Destroy all rooms containing the given location */ bool borg_clear_room(int y, int x) { uint xx, yy; auto_grid *ag; auto_room *ar; bool res = FALSE; /* Absorb old rooms */ for (ar = room(1, y, x); ar; ar = room(0, 0, 0)) { /* Scan the "contained" room */ for (yy = ar->y1; yy <= ar->y2; yy++) { for (xx = ar->x1; xx <= ar->x2; xx++) { /* Get the "contained" grid */ ag = &auto_grids[yy][xx]; /* Normal grids "lose" their parents. */ if (ag->room < AUTO_ROOMS) ag->room = 0; /* Cross-rooms lose one parent */ if (ag->room > AUTO_ROOMS) ag->room--; } } /* That room is now "gone" */ ar->when = 0L; ar->x1 = ar->x2 = 0; ar->y1 = ar->y2 = 0; /* Add the room to the "free list" */ auto_rooms[ar->self].free = auto_rooms[0].free; /* Add the room to the "free list" */ auto_rooms[0].free = ar->self; } /* Result */ return (res); } /* * Clear out the "room" array */ void borg_wipe_rooms(void) { int i, x, y; auto_grid *ag; auto_room *ar; /* Clean up the old used rooms */ for (i = 1; i < auto_room_max; i++) { /* Access the room */ ar = &auto_rooms[i]; /* Place the room in the free room list */ ar->free = i + 1; /* No location */ ar->x1 = ar->x2 = ar->y1 = ar->y2 = 0; /* Never seen it */ ar->when = 0L; } /* Reset the free list */ auto_room_head->free = 1; /* Maximum room index */ auto_room_max = 1; } #endif /* * A simple, fast, integer-based line-of-sight algorithm. * * See "los()" in "cave.c" for complete documentation */ bool borg_los(int y1, int x1, int y2, int x2) { /* Delta */ int dx, dy; /* Absolute */ int ax, ay; /* Signs */ int sx, sy; /* Fractions */ int qx, qy; /* Scanners */ int tx, ty; /* Scale factors */ int f1, f2; /* Slope, or 1/Slope, of LOS */ int m; /* Extract the offset */ dy = y2 - y1; dx = x2 - x1; /* Extract the absolute offset */ ay = ABS(dy); ax = ABS(dx); /* Handle adjacent (or identical) grids */ if ((ax < 2) && (ay < 2)) return (TRUE); /* Paranoia -- require "safe" origin */ /* if (!in_bounds(y1, x1)) return (FALSE); */ /* Directly South/North */ if (!dx) { /* South -- check for walls */ if (dy > 0) { for (ty = y1 + 1; ty < y2; ty++) { if (!borg_cave_floor_bold(ty, x1)) return (FALSE); } } /* North -- check for walls */ else { for (ty = y1 - 1; ty > y2; ty--) { if (!borg_cave_floor_bold(ty, x1)) return (FALSE); } } /* Assume los */ return (TRUE); } /* Directly East/West */ if (!dy) { /* East -- check for walls */ if (dx > 0) { for (tx = x1 + 1; tx < x2; tx++) { if (!borg_cave_floor_bold(y1, tx)) return (FALSE); } } /* West -- check for walls */ else { for (tx = x1 - 1; tx > x2; tx--) { if (!borg_cave_floor_bold(y1, tx)) return (FALSE); } } /* Assume los */ return (TRUE); } /* Extract some signs */ sx = (dx < 0) ? -1 : 1; sy = (dy < 0) ? -1 : 1; /* Vertical "knights" */ if (ax == 1) { if (ay == 2) { if (borg_cave_floor_bold(y1 + sy, x1)) return (TRUE); } } /* Horizontal "knights" */ else if (ay == 1) { if (ax == 2) { if (borg_cave_floor_bold(y1, x1 + sx)) return (TRUE); } } /* Calculate scale factor div 2 */ f2 = (ax * ay); /* Calculate scale factor */ f1 = f2 << 1; /* Travel horizontally */ if (ax >= ay) { /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */ qy = ay * ay; m = qy << 1; tx = x1 + sx; /* Consider the special case where slope == 1. */ if (qy == f2) { ty = y1 + sy; qy -= f1; } else { ty = y1; } /* Note (below) the case (qy == f2), where */ /* the LOS exactly meets the corner of a tile. */ while (x2 - tx) { if (!borg_cave_floor_bold(ty, tx)) return (FALSE); qy += m; if (qy < f2) { tx += sx; } else if (qy > f2) { ty += sy; if (!borg_cave_floor_bold(ty, tx)) return (FALSE); qy -= f1; tx += sx; } else { ty += sy; qy -= f1; tx += sx; } } } /* Travel vertically */ else { /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */ qx = ax * ax; m = qx << 1; ty = y1 + sy; if (qx == f2) { tx = x1 + sx; qx -= f1; } else { tx = x1; } /* Note (below) the case (qx == f2), where */ /* the LOS exactly meets the corner of a tile. */ while (y2 - ty) { if (!borg_cave_floor_bold(ty, tx)) return (FALSE); qx += m; if (qx < f2) { ty += sy; } else if (qx > f2) { tx += sx; if (!borg_cave_floor_bold(ty, tx)) return (FALSE); qx -= f1; ty += sy; } else { tx += sx; qx -= f1; ty += sy; } } } /* Assume los */ return (TRUE); } /* * Check the projection from (x1,y1) to (x2,y2). * Assume that there is no monster in the way. * Hack -- we refuse to assume that unknown grids are floors * Adapted from "projectable()" in "spells1.c". */ bool borg_projectable(int y1, int x1, int y2, int x2) { int dist, y, x; auto_grid *ag; /* Start at the initial location */ y = y1; x = x1; /* Simulate the spell/missile path */ for (dist = 0; dist <= MAX_RANGE; dist++) { /* Get the grid */ ag = &auto_grids[y][x]; /* Assume all unknow grids more than distance 10 from you */ /* are walls. */ if ((dist > 10) && (ag->feat == FEAT_NONE)) break; /* Never pass through walls/doors */ if (dist && (!borg_cave_floor_grid(ag))) break; /* Check for arrival at "final target" */ if ((x == x2) && (y == y2)) return (TRUE); /* Calculate the new location */ mmove2(&y, &x, y1, x1, y2, x2); } /* Assume obstruction */ return (FALSE); } /* * Check the projection from (x1,y1) to (x2,y2). * Assume that monsters in the way will stop the projection * Hack -- we refuse to assume that unknown grids are floors * Adapted from "projectable()" in "spells1.c". */ bool borg_projectable_pure(int y1, int x1, int y2, int x2) { int dist, y, x; auto_grid *ag; /* Start at the initial location */ y = y1; x = x1; /* Simulate the spell/missile path */ for (dist = 0; dist <= MAX_RANGE; dist++) { /* Get the grid */ ag = &auto_grids[y][x]; /* Hack -- assume unknown grids are walls */ if (dist && (ag->feat == FEAT_NONE)) break; /* Never pass through walls/doors */ if (dist && (!borg_cave_floor_grid(ag))) break; /* Check for arrival at "final target" */ if ((x == x2) && (y == y2)) return (TRUE); /* Stop at monsters */ if (ag->kill) break; /* Calculate the new location */ mmove2(&y, &x, y1, x1, y2, x2); } /* Assume obstruction */ return (FALSE); } /* * Clear the lite grids */ void borg_forget_lite(void) { int i; /* None to forget */ if (!auto_lite_n) return; /* Clear them all */ for (i = 0; i < auto_lite_n; i++) { int y = auto_lite_y[i]; int x = auto_lite_x[i]; /* Forget that the grid is lit */ auto_grids[y][x].info &= ~BORG_LITE; } /* None left */ auto_lite_n = 0; } /* * XXX XXX XXX * * This macro allows us to efficiently add a grid to the "lite" array, * note that we are never called for illegal grids, or for grids which * have already been placed into the "lite" array, and we are never * called when the "lite" array is full. */ #define borg_cave_lite_hack(Y,X) \ auto_grids[Y][X].info |= BORG_LITE; \ auto_lite_y[auto_lite_n] = (Y); \ auto_lite_x[auto_lite_n] = (X); \ auto_lite_n++ /* * Update the set of grids "illuminated" by the player's lite. * * See "update_lite" in "cave.c" for complete documentation * * It is very important that the "player grid" be the first grid in the * array of "BORG_LITE" grids, since this is assumed in several places. */ void borg_update_lite(void) { int i, x, y, min_x, max_x, min_y, max_y; /*** Clear old grids ***/ /* Clear them all */ for (i = 0; i < auto_lite_n; i++) { y = auto_lite_y[i]; x = auto_lite_x[i]; /* Mark the grid as not "lite" */ auto_grids[y][x].info &= ~BORG_LITE; } /* None left */ auto_lite_n = 0; /* Hack -- Player has no lite */ if (my_cur_lite <= 0) return; /*** Collect the new "lite" grids ***/ /* Player grid */ borg_cave_lite_hack(c_y, c_x); /* Radius 1 -- torch radius */ if (my_cur_lite >= 1) { /* Adjacent grid */ borg_cave_lite_hack(c_y+1, c_x); borg_cave_lite_hack(c_y-1, c_x); borg_cave_lite_hack(c_y, c_x+1); borg_cave_lite_hack(c_y, c_x-1); /* Diagonal grids */ borg_cave_lite_hack(c_y+1, c_x+1); borg_cave_lite_hack(c_y+1, c_x-1); borg_cave_lite_hack(c_y-1, c_x+1); borg_cave_lite_hack(c_y-1, c_x-1); } /* Radius 2 -- lantern radius */ if (my_cur_lite >= 2) { /* South of the player */ if (borg_cave_floor_bold(c_y+1, c_x)) { borg_cave_lite_hack(c_y+2, c_x); borg_cave_lite_hack(c_y+2, c_x+1); borg_cave_lite_hack(c_y+2, c_x-1); } /* North of the player */ if (borg_cave_floor_bold(c_y-1, c_x)) { borg_cave_lite_hack(c_y-2, c_x); borg_cave_lite_hack(c_y-2, c_x+1); borg_cave_lite_hack(c_y-2, c_x-1); } /* East of the player */ if (borg_cave_floor_bold(c_y, c_x+1)) { borg_cave_lite_hack(c_y, c_x+2); borg_cave_lite_hack(c_y+1, c_x+2); borg_cave_lite_hack(c_y-1, c_x+2); } /* West of the player */ if (borg_cave_floor_bold(c_y, c_x-1)) { borg_cave_lite_hack(c_y, c_x-2); borg_cave_lite_hack(c_y+1, c_x-2); borg_cave_lite_hack(c_y-1, c_x-2); } } /* Radius 3+ -- artifact radius */ if (my_cur_lite >= 3) { int d, p; /* Maximal radius */ p = my_cur_lite; /* Paranoia -- see "LITE_MAX" */ if (p > 5) p = 5; /* South-East of the player */ if (borg_cave_floor_bold(c_y+1, c_x+1)) { borg_cave_lite_hack(c_y+2, c_x+2); } /* South-West of the player */ if (borg_cave_floor_bold(c_y+1, c_x-1)) { borg_cave_lite_hack(c_y+2, c_x-2); } /* North-East of the player */ if (borg_cave_floor_bold(c_y-1, c_x+1)) { borg_cave_lite_hack(c_y-2, c_x+2); } /* North-West of the player */ if (borg_cave_floor_bold(c_y-1, c_x-1)) { borg_cave_lite_hack(c_y-2, c_x-2); } /* Maximal north */ min_y = c_y - p; if (min_y < 0) min_y = 0; /* Maximal south */ max_y = c_y + p; if (max_y > AUTO_MAX_Y-1) max_y = AUTO_MAX_Y-1; /* Maximal west */ min_x = c_x - p; if (min_x < 0) min_x = 0; /* Maximal east */ max_x = c_x + p; if (max_x > AUTO_MAX_X-1) max_x = AUTO_MAX_X-1; /* Scan the maximal box */ for (y = min_y; y <= max_y; y++) { for (x = min_x; x <= max_x; x++) { int dy = (c_y > y) ? (c_y - y) : (y - c_y); int dx = (c_x > x) ? (c_x - x) : (x - c_x); /* Skip the "central" grids (above) */ if ((dy <= 2) && (dx <= 2)) continue; /* Hack -- approximate the distance */ d = (dy > dx) ? (dy + (dx>>1)) : (dx + (dy>>1)); /* Skip distant grids */ if (d > p) continue; /* Viewable, nearby, grids get "torch lit" */ if (auto_grids[y][x].info & BORG_VIEW) { /* This grid is "torch lit" */ borg_cave_lite_hack(y, x); } } } } } /* * Clear the viewable space */ void borg_forget_view(void) { int i; auto_grid *ag; /* None to forget */ if (!auto_view_n) return; /* Clear them all */ for (i = 0; i < auto_view_n; i++) { int y = auto_view_y[i]; int x = auto_view_x[i]; /* Access the grid */ ag = &auto_grids[y][x]; /* Forget that the grid is viewable */ ag->info &= ~BORG_VIEW; } /* None left */ auto_view_n = 0; } /* * This macro allows us to efficiently add a grid to the "view" array, * note that we are never called for illegal grids, or for grids which * have already been placed into the "view" array, and we are never * called when the "view" array is full. */ #define borg_cave_view_hack(A,Y,X) \ (A)->info |= BORG_VIEW; \ auto_view_y[auto_view_n] = (Y); \ auto_view_x[auto_view_n] = (X); \ auto_view_n++ /* * Helper function for "borg_update_view()" below * * See "update_view_aux()" in "cave.c" for complete documentation. */ static bool borg_update_view_aux(int y, int x, int y1, int x1, int y2, int x2) { bool f1, f2, v1, v2, z1, z2, wall; auto_grid *ag; auto_grid *g1_ag; auto_grid *g2_ag; /* Access the grids */ g1_ag = &auto_grids[y1][x1]; g2_ag = &auto_grids[y2][x2]; /* Check for walls */ f1 = (borg_cave_floor_grid(g1_ag)); f2 = (borg_cave_floor_grid(g2_ag)); /* Totally blocked by physical walls */ if (!f1 && !f2) return (TRUE); /* Check for visibility */ v1 = (f1 && (g1_ag->info & BORG_VIEW)); v2 = (f2 && (g2_ag->info & BORG_VIEW)); /* Totally blocked by "unviewable neighbors" */ if (!v1 && !v2) return (TRUE); /* Access the grid */ ag = &auto_grids[y][x]; /* Check for walls */ wall = (!borg_cave_floor_grid(ag)); /* Check the "ease" of visibility */ z1 = (v1 && (g1_ag->info & BORG_XTRA)); z2 = (v2 && (g2_ag->info & BORG_XTRA)); /* Hack -- "easy" plus "easy" yields "easy" */ if (z1 && z2) { ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y, x); return (wall); } /* Hack -- primary "easy" yields "viewed" */ if (z1) { borg_cave_view_hack(ag, y, x); return (wall); } /* Hack -- "view" plus "view" yields "view" */ if (v1 && v2) { /* ag->info |= BORG_XTRA; */ borg_cave_view_hack(ag, y, x); return (wall); } /* Mega-Hack -- the "borg_los()" function works poorly on walls */ if (wall) { borg_cave_view_hack(ag, y, x); return (wall); } /* Hack -- check line of sight */ if (borg_los(c_y, c_x, y, x)) { borg_cave_view_hack(ag, y, x); return (wall); } /* Assume no line of sight. */ return (TRUE); } /* * Calculate the region currently "viewable" by the player * * See "update_view()" in "cave.c" for complete documentation * * It is very important that the "player grid" be the first grid in the * array of "BORG_VIEW" grids, since this is assumed in several places. */ void borg_update_view(void) { int n, m, d, k, y, x, z; int se, sw, ne, nw, es, en, ws, wn; int full, over; auto_grid *ag; /*** Initialize ***/ /* Optimize */ if (view_reduce_view && !auto_depth) { /* Full radius (10) */ full = MAX_SIGHT / 2; /* Octagon factor (15) */ over = MAX_SIGHT * 3 / 4; } /* Normal */ else { /* Full radius (20) */ full = MAX_SIGHT; /* Octagon factor (30) */ over = MAX_SIGHT * 3 / 2; } /*** Step 0 -- Begin ***/ /* Save the old "view" grids for later */ for (n = 0; n < auto_view_n; n++) { y = auto_view_y[n]; x = auto_view_x[n]; /* Access the grid */ ag = &auto_grids[y][x]; /* Mark the grid as not in "view" */ ag->info &= ~(BORG_VIEW); } /* Start over with the "view" array */ auto_view_n = 0; /*** Step 1 -- adjacent grids ***/ /* Now start on the player */ y = c_y; x = c_x; /* Access the grid */ ag = &auto_grids[y][x]; /* Assume the player grid is easily viewable */ ag->info |= BORG_XTRA; /* Assume the player grid is viewable */ borg_cave_view_hack(ag, y, x); /*** Step 2 -- Major Diagonals ***/ /* Hack -- Limit */ z = full * 2 / 3; /* Scan south-east */ for (d = 1; d <= z; d++) { ag = &auto_grids[y+d][x+d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y+d, x+d); if (!borg_cave_floor_grid(ag)) break; } /* Scan south-west */ for (d = 1; d <= z; d++) { ag = &auto_grids[y+d][x-d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y+d, x-d); if (!borg_cave_floor_grid(ag)) break; } /* Scan north-east */ for (d = 1; d <= z; d++) { ag = &auto_grids[y-d][x+d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y-d, x+d); if (!borg_cave_floor_grid(ag)) break; } /* Scan north-west */ for (d = 1; d <= z; d++) { ag = &auto_grids[y-d][x-d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y-d, x-d); if (!borg_cave_floor_grid(ag)) break; } /*** Step 3 -- major axes ***/ /* Scan south */ for (d = 1; d <= full; d++) { ag = &auto_grids[y+d][x]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y+d, x); if (!borg_cave_floor_grid(ag)) break; } /* Initialize the "south strips" */ se = sw = d; /* Scan north */ for (d = 1; d <= full; d++) { ag = &auto_grids[y-d][x]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y-d, x); if (!borg_cave_floor_grid(ag)) break; } /* Initialize the "north strips" */ ne = nw = d; /* Scan east */ for (d = 1; d <= full; d++) { ag = &auto_grids[y][x+d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y, x+d); if (!borg_cave_floor_grid(ag)) break; } /* Initialize the "east strips" */ es = en = d; /* Scan west */ for (d = 1; d <= full; d++) { ag = &auto_grids[y][x-d]; ag->info |= BORG_XTRA; borg_cave_view_hack(ag, y, x-d); if (!borg_cave_floor_grid(ag)) break; } /* Initialize the "west strips" */ ws = wn = d; /*** Step 4 -- Divide each "octant" into "strips" ***/ /* Now check each "diagonal" (in parallel) */ for (n = 1; n <= over / 2; n++) { int ypn, ymn, xpn, xmn; /* Acquire the "bounds" of the maximal circle */ z = over - n - n; if (z > full - n) z = full - n; while ((z + n + (n>>1)) > full) z--; /* Access the four diagonal grids */ ypn = y + n; ymn = y - n; xpn = x + n; xmn = x - n; /* South strip */ if (ypn < AUTO_MAX_Y-1) { /* Maximum distance */ m = MIN(z, (AUTO_MAX_Y-1) - ypn); /* East side */ if ((xpn <= AUTO_MAX_X-1) && (n < se)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ypn+d, xpn, ypn+d-1, xpn-1, ypn+d-1, xpn)) { if (n + d >= se) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ se = k + 1; } /* West side */ if ((xmn >= 0) && (n < sw)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ypn+d, xmn, ypn+d-1, xmn+1, ypn+d-1, xmn)) { if (n + d >= sw) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ sw = k + 1; } } /* North strip */ if (ymn > 0) { /* Maximum distance */ m = MIN(z, ymn); /* East side */ if ((xpn <= AUTO_MAX_X-1) && (n < ne)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ymn-d, xpn, ymn-d+1, xpn-1, ymn-d+1, xpn)) { if (n + d >= ne) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ ne = k + 1; } /* West side */ if ((xmn >= 0) && (n < nw)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ymn-d, xmn, ymn-d+1, xmn+1, ymn-d+1, xmn)) { if (n + d >= nw) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ nw = k + 1; } } /* East strip */ if (xpn < AUTO_MAX_X-1) { /* Maximum distance */ m = MIN(z, (AUTO_MAX_X-1) - xpn); /* South side */ if ((ypn <= AUTO_MAX_Y-1) && (n < es)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ypn, xpn+d, ypn-1, xpn+d-1, ypn, xpn+d-1)) { if (n + d >= es) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ es = k + 1; } /* North side */ if ((ymn >= 0) && (n < en)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ymn, xpn+d, ymn+1, xpn+d-1, ymn, xpn+d-1)) { if (n + d >= en) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ en = k + 1; } } /* West strip */ if (xmn > 0) { /* Maximum distance */ m = MIN(z, xmn); /* South side */ if ((ypn <= AUTO_MAX_Y-1) && (n < ws)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ypn, xmn-d, ypn-1, xmn-d+1, ypn, xmn-d+1)) { if (n + d >= ws) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ ws = k + 1; } /* North side */ if ((ymn >= 0) && (n < wn)) { /* Scan */ for (k = n, d = 1; d <= m; d++) { /* Check grid "d" in strip "n", notice "blockage" */ if (borg_update_view_aux(ymn, xmn-d, ymn+1, xmn-d+1, ymn, xmn-d+1)) { if (n + d >= wn) break; } /* Track most distant "non-blockage" */ else { k = n + d; } } /* Limit the next strip */ wn = k + 1; } } } /*** Step 5 -- Complete the algorithm ***/ /* Update all the new grids */ for (n = 0; n < auto_view_n; n++) { y = auto_view_y[n]; x = auto_view_x[n]; /* Access the grid */ ag = &auto_grids[y][x]; /* Clear the "BORG_XTRA" flag */ ag->info &= ~BORG_XTRA; } } /* * Init this file. */ void borg_init_2(void) { /* Nothing */ } #else #ifdef MACINTOSH static int HACK = 0; #endif #endif