/* * Initialize the "vinfo" array * * Full Octagon (radius 20), Grids=1149 * * Quadrant (south east), Grids=308, Slopes=251 * * Octant (east then south), Grids=161, Slopes=126 * * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES * have the correct values, which can be derived by setting them to * a number which is too high, running this function, and using the * error messages to obtain the correct values. */ public static int vinfo_init() { int i, g; long m; int num_grids = 0; int queue_head = 0; int queue_tail = 0; vinfo_type[] queue = new vinfo_type[VINFO_MAX_GRIDS*2]; //Nick: initialize v_info here, fill it with new stuffs to play with instead of nulls for (int n = 0; n < vinfo.Count(); n++){ vinfo[n] = new vinfo_type(); } /* Make hack */ vinfo_hack hack = new vinfo_hack(); /* Analyze grids */ for (int y = 0; y <= Misc.MAX_SIGHT; ++y) { //Triangular array? Weird... Triangle is on top right corner for (int x = y; x <= Misc.MAX_SIGHT; ++x) { /* Skip grids which are out of sight range */ if (distance(0, 0, y, x) > Misc.MAX_SIGHT) continue; /* Default slope range */ hack.slopes_min[y,x] = 999999999; hack.slopes_max[y,x] = 0; /* Paranoia */ if (num_grids >= VINFO_MAX_GRIDS) { Utilities.quit("Too many grids (" + num_grids + " >= " + VINFO_MAX_GRIDS + ")!"); } /* Count grids */ num_grids++; /* Slope to the top right corner */ m = SCALE * (1000L * y - 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, y, x, m); /* Slope to top left corner */ m = SCALE * (1000L * y - 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, y, x, m); /* Slope to bottom right corner */ m = SCALE * (1000L * y + 500) / (1000L * x + 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, y, x, m); /* Slope to bottom left corner */ m = SCALE * (1000L * y + 500) / (1000L * x - 500); /* Handle "legal" slopes */ vinfo_init_aux(hack, y, x, m); } } /* Enforce maximal efficiency */ if (num_grids < VINFO_MAX_GRIDS) { Utilities.quit("Too few grids (" + num_grids + " < " + VINFO_MAX_GRIDS + ")!"); } /* Enforce maximal efficiency */ if (hack.num_slopes < VINFO_MAX_SLOPES) { Utilities.quit("Too few slopes (" + hack.num_slopes + " < " + VINFO_MAX_SLOPES + ")!"); } Utilities.sort(hack.slopes, cmp_longs); /* Enqueue player grid */ queue[queue_tail++] = vinfo[0]; /* Process queue */ while (queue_head < queue_tail) { int e; /* Index */ e = queue_head++; /* Main Grid */ g = vinfo[e].grid[0]; /* Location */ int y = GRID_Y(g); int x = GRID_X(g); /* Compute grid offsets */ vinfo[e].grid[0] = (short)GRID(+y,+x); vinfo[e].grid[1] = (short)GRID(+x,+y); vinfo[e].grid[2] = (short)GRID(+x,-y); vinfo[e].grid[3] = (short)GRID(+y,-x); vinfo[e].grid[4] = (short)GRID(-y,-x); vinfo[e].grid[5] = (short)GRID(-x,-y); vinfo[e].grid[6] = (short)GRID(-x,+y); vinfo[e].grid[7] = (short)GRID(-y,+x); /* Analyze slopes */ for (i = 0; i < hack.num_slopes; ++i) { m = hack.slopes[i]; /* Memorize intersection slopes (for non-player-grids) */ if ((e > 0) && (hack.slopes_min[y,x] < m) && (m < hack.slopes_max[y,x])) { switch (i / 32) { case 3: vinfo[e].bits_3 |= (uint)(1L << (i % 32)); break; case 2: vinfo[e].bits_2 |= (uint)(1L << (i % 32)); break; case 1: vinfo[e].bits_1 |= (uint)(1L << (i % 32)); break; case 0: vinfo[e].bits_0 |= (uint)(1L << (i % 32)); break; } } } /* Default */ vinfo[e].next_0 = vinfo[0]; /* Grid next child */ if (distance(0, 0, y, x+1) <= Misc.MAX_SIGHT) { g = GRID(y,x+1); if (queue[queue_tail-1].grid[0] != g) { vinfo[queue_tail].grid[0] = (short)g; queue[queue_tail] = vinfo[queue_tail]; queue_tail++; } vinfo[e].next_0 = vinfo[queue_tail - 1]; } /* Default */ vinfo[e].next_1 = vinfo[0]; /* Grid diag child */ if (distance(0, 0, y+1, x+1) <= Misc.MAX_SIGHT) { g = GRID(y+1,x+1); if (queue[queue_tail-1].grid[0] != g) { vinfo[queue_tail].grid[0] = (short)g; queue[queue_tail] = vinfo[queue_tail]; queue_tail++; } vinfo[e].next_1 = vinfo[queue_tail - 1]; } /* Hack -- main diagonal has special children */ if (y == x) vinfo[e].next_0 = vinfo[e].next_1; /* Extra values */ vinfo[e].y = (byte)y; vinfo[e].x = (byte)x; vinfo[e].d = (byte)((y > x) ? (y + x/2) : (x + y/2)); vinfo[e].r = (byte)((y==0) ? x : (x==0) ? y : (y == x) ? y : 0); } /* Verify maximal bits XXX XXX XXX */ if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) || ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) || ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) || ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0)) { Utilities.quit("Incorrect bit masks!"); } /* Kill hack */ //FREE(hack); //lolc /* Success */ return (0); }
/* * Calculate the complete field of view using a new algorithm * * If "view_g" and "temp_g" were global pointers to arrays of grids, as * opposed to actual arrays of grids, then we could be more efficient by * using "pointer swapping". * * Note the following idiom, which is used in the function below. * This idiom processes each "octant" of the field of view, in a * clockwise manner, starting with the east strip, south side, * and for each octant, allows a simple calculation to set "g" * equal to the proper grids, relative to "pg", in the octant. * * for (o2 = 0; o2 < 8; o2++) * ... * g = pg + p.grid[o2]; * ... * * * Normally, vision along the major axes is more likely than vision * along the diagonal axes, so we check the bits corresponding to * the lines of sight near the major axes first. * * We use the "temp_g" array (and the "CAVE_TEMP" flag) to keep track of * which grids were previously marked "CAVE_SEEN", since only those grids * whose "CAVE_SEEN" value changes during this routine must be redrawn. * * This function is now responsible for maintaining the "CAVE_SEEN" * flags as well as the "CAVE_VIEW" flags, which is good, because * the only grids which normally need to be memorized and/or redrawn * are the ones whose "CAVE_SEEN" flag changes during this routine. * * Basically, this function divides the "octagon of view" into octants of * grids (where grids on the main axes and diagonal axes are "shared" by * two octants), and processes each octant one at a time, processing each * octant one grid at a time, processing only those grids which "might" be * viewable, and setting the "CAVE_VIEW" flag for each grid for which there * is an (unobstructed) line of sight from the center of the player grid to * any internal point in the grid (and collecting these "CAVE_VIEW" grids * into the "view_g" array), and setting the "CAVE_SEEN" flag for the grid * if, in addition, the grid is "illuminated" in some way. * * This function relies on a theorem (suggested and proven by Mat Hostetter) * which states that in each octant of a field of view, a given grid will * be "intersected" by one or more unobstructed "lines of sight" from the * center of the player grid if and only if it is "intersected" by at least * one such unobstructed "line of sight" which passes directly through some * corner of some grid in the octant which is not shared by any other octant. * The proof is based on the fact that there are at least three significant * lines of sight involving any non-shared grid in any octant, one which * intersects the grid and passes though the corner of the grid closest to * the player, and two which "brush" the grid, passing through the "outer" * corners of the grid, and that any line of sight which intersects a grid * without passing through the corner of a grid in the octant can be "slid" * slowly towards the corner of the grid closest to the player, until it * either reaches it or until it brushes the corner of another grid which * is closer to the player, and in either case, the existanc of a suitable * line of sight is thus demonstrated. * * It turns out that in each octant of the radius 20 "octagon of view", * there are 161 grids (with 128 not shared by any other octant), and there * are exactly 126 distinct "lines of sight" passing from the center of the * player grid through any corner of any non-shared grid in the octant. To * determine if a grid is "viewable" by the player, therefore, you need to * simply show that one of these 126 lines of sight intersects the grid but * does not intersect any wall grid closer to the player. So we simply use * a bit vector with 126 bits to represent the set of interesting lines of * sight which have not yet been obstructed by wall grids, and then we scan * all the grids in the octant, moving outwards from the player grid. For * each grid, if any of the lines of sight which intersect that grid have not * yet been obstructed, then the grid is viewable. Furthermore, if the grid * is a wall grid, then all of the lines of sight which intersect the grid * should be marked as obstructed for future reference. Also, we only need * to check those grids for whom at least one of the "parents" was a viewable * non-wall grid, where the parents include the two grids touching the grid * but closer to the player grid (one adjacent, and one diagonal). For the * bit vector, we simply use 4 32-bit integers. All of the static values * which are needed by this function are stored in the large "vinfo" array * (above), which is machine generated by another program. XXX XXX XXX * * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids * because the grids at the edge of the field of view use "grid zero" as * their children, and the queue must be able to hold several of these * special grids. Because the actual number of required grids is bizarre, * we simply allocate twice as many as we would normally need. XXX XXX XXX */ public static void update_view() { int py = Misc.p_ptr.py; int px = Misc.p_ptr.px; int pg = GRID(py,px); int i, j, k, g, o2; int radius; int fast_view_n = view_n; ushort[] fast_view_g = view_g; int fast_temp_n = 0; ushort[] fast_temp_g = Misc.temp_g; /* XXX: also moronic. Optimizers exist. */ //byte *fast_cave_info = &cave.info[0][0]; //lolz, Nick: I am omitting this short info; /*** Step 0 -- Begin ***/ /* Save the old "view" grids for later */ for (i = 0; i < fast_view_n; i++) { /* Grid */ g = fast_view_g[i]; int x = GRID_X(g); int y = GRID_Y(g); /* Get grid info */ //info = fast_cave_info[g]; info = cave.info[y][x]; /* Save "CAVE_SEEN" grids */ if ((info & (CAVE_SEEN)) != 0) { /* Set "CAVE_TEMP" flag */ info |= (CAVE_TEMP); //NICK This is a hack, if we have seen it, we mark it... info |= (CAVE_MARK); /* Save grid for later */ fast_temp_g[fast_temp_n++] = (ushort)g; } /* Clear "CAVE_VIEW" and "CAVE_SEEN" flags */ info &= ~(CAVE_VIEW | CAVE_SEEN); /* Clear "CAVE_LIGHT" flag */ /* info &= ~(CAVE_LIGHT); */ /* Save cave info */ //fast_cave_info[g] = info; cave.info[y][x] = info; } /* Reset the "view" array */ fast_view_n = 0; /* Extract "radius" value */ radius = Misc.p_ptr.cur_light; /* Handle real light */ if (radius > 0) ++radius; /* Scan monster list and add monster lights */ for (k = 1; k < Misc.z_info.m_max; k++) { /* Check the k'th monster */ Monster.Monster m_ptr = cave_monster(cave, k); if(m_ptr == null) continue; //TODO: See why there were null monsters in the cave? Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; /* Access the location */ int fx = m_ptr.fx; int fy = m_ptr.fy; bool in_los = los(Misc.p_ptr.py, Misc.p_ptr.px, fy, fx); /* Skip dead monsters */ if (m_ptr.r_idx == 0) continue; /* Skip monsters not carrying light */ if (!r_ptr.flags.has(Monster_Flag.HAS_LIGHT.value)) continue; /* Light a 3x3 box centered on the monster */ for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { int sy = fy + i; int sx = fx + j; /* If the monster isn't visible we can only light open tiles */ if (!in_los && !cave_floor_bold(sy, sx)) continue; /* If the tile is too far away we won't light it */ if (distance(Misc.p_ptr.py, Misc.p_ptr.px, sy, sx) > Misc.MAX_SIGHT) continue; /* If the tile itself isn't in LOS, don't light it */ if (!los(Misc.p_ptr.py, Misc.p_ptr.px, sy, sx)) continue; g = GRID(sy, sx); /* Mark the square lit and seen */ //fast_cave_info[g] |= (CAVE_VIEW | CAVE_SEEN); cave.info[sy][sx] |= (CAVE_VIEW | CAVE_SEEN); /* Save in array */ fast_view_g[fast_view_n++] = (ushort)g; } } } /*** Step 1 -- player grid ***/ /* Player grid */ g = pg; /* Get grid info */ //info = fast_cave_info[g]; int tx = GRID_X(g); int ty = GRID_Y(g); info = cave.info[ty][tx]; /* Assume viewable */ info |= (CAVE_VIEW); /* Torch-lit grid */ if (0 < radius) { /* Mark as "CAVE_SEEN" */ info |= (CAVE_SEEN); /* Mark as "CAVE_LIGHT" */ /* info |= (CAVE_LIGHT); */ } /* Perma-lit grid */ else if ((info & (CAVE_GLOW)) != 0) { /* Mark as "CAVE_SEEN" */ info |= (CAVE_SEEN); } /* Save cave info */ //fast_cave_info[g] = info; cave.info[GRID_Y(g)][GRID_X(g)] = info; /* Save in array */ fast_view_g[fast_view_n++] = (ushort)g; /*** Step 2 -- octants ***/ /* Scan each octant */ for (o2 = 0; o2 < 8; o2++) { vinfo_type p; /* Last added */ vinfo_type last = vinfo[0]; /* Grid queue */ int queue_head = 0; int queue_tail = 0; vinfo_type[] queue = new vinfo_type[VINFO_MAX_GRIDS*2]; for(int q = 0; q < queue.Length; q++) { queue[q] = new vinfo_type(); //Gotta do this to avoid null references... } /* Slope bit vector */ uint bits0 = (uint)VINFO_BITS_0; uint bits1 = (uint)VINFO_BITS_1; uint bits2 = (uint)VINFO_BITS_2; uint bits3 = (uint)VINFO_BITS_3; /* Reset queue */ queue_head = queue_tail = 0; /* Initial grids */ queue[queue_tail++] = vinfo[1]; queue[queue_tail++] = vinfo[2]; /* Process queue */ while (queue_head < queue_tail) { /* Dequeue next grid */ p = queue[queue_head++]; /* Check bits */ if ((bits0 & (p.bits_0)) != 0 || (bits1 & (p.bits_1)) != 0 || (bits2 & (p.bits_2)) != 0 || (bits3 & (p.bits_3)) != 0) { /* Extract grid value XXX XXX XXX */ g = pg + p.grid[o2]; /* Get grid info */ //info = fast_cave_info[g]; info = cave.info[GRID_Y(g)][GRID_X(g)]; /* Handle wall */ if ((info & (CAVE_WALL)) != 0) { /* Clear bits */ bits0 &= ~(p.bits_0); bits1 &= ~(p.bits_1); bits2 &= ~(p.bits_2); bits3 &= ~(p.bits_3); /* Newly viewable wall */ if ((info & (CAVE_VIEW)) == 0) { /* Mark as viewable */ info |= (CAVE_VIEW); /* Torch-lit grids */ if (p.d < radius) { /* Mark as "CAVE_SEEN" */ info |= (CAVE_SEEN); /* Mark as "CAVE_LIGHT" */ /* info |= (CAVE_LIGHT); */ } /* Perma-lit grids */ else if ((info & (CAVE_GLOW)) != 0) { int y = GRID_Y(g); int x = GRID_X(g); /* Hack -- move towards player */ int yy = (y < py) ? (y + 1) : (y > py) ? (y - 1) : y; int xx = (x < px) ? (x + 1) : (x > px) ? (x - 1) : x; /* Check for "simple" illumination */ if ((cave.info[yy][xx] & (CAVE_GLOW)) != 0) { /* Mark as seen */ info |= (CAVE_SEEN); } } /* Save cave info */ //fast_cave_info[g] = info; cave.info[GRID_Y(g)][GRID_X(g)] = info; /* Save in array */ fast_view_g[fast_view_n++] = (ushort)g; } } /* Handle non-wall */ else { /* Enqueue child */ if (last != p.next_0) { queue[queue_tail++] = last = p.next_0; } /* Enqueue child */ if (last != p.next_1) { queue[queue_tail++] = last = p.next_1; } /* Newly viewable non-wall */ if ((info & (CAVE_VIEW)) == 0) { /* Mark as "viewable" */ info |= (CAVE_VIEW); /* Torch-lit grids */ if (p.d < radius) { /* Mark as "CAVE_SEEN" */ info |= (CAVE_SEEN); /* Mark as "CAVE_LIGHT" */ /* info |= (CAVE_LIGHT); */ } /* Perma-lit grids */ else if ((info & (CAVE_GLOW)) != 0) { /* Mark as "CAVE_SEEN" */ info |= (CAVE_SEEN); } /* Save cave info */ //fast_cave_info[g] = info; cave.info[GRID_Y(g)][GRID_X(g)] = info; /* Save in array */ fast_view_g[fast_view_n++] = (ushort)g; } } } } } /*** Step 3 -- Complete the algorithm ***/ /* Handle blindness */ if (Misc.p_ptr.timed[(int)Timed_Effect.BLIND] != 0) { /* Process "new" grids */ for (i = 0; i < fast_view_n; i++) { /* Grid */ g = fast_view_g[i]; /* Grid cannot be "CAVE_SEEN" */ //fast_cave_info[g] &= ~(CAVE_SEEN); cave.info[GRID_Y(g)][GRID_X(g)] &= ~(CAVE_SEEN); } } /* Process "new" grids */ for (i = 0; i < fast_view_n; i++) { /* Grid */ g = fast_view_g[i]; /* Get grid info */ //info = fast_cave_info[g]; info = cave.info[GRID_Y(g)][GRID_X(g)]; /* Was not "CAVE_SEEN", is now "CAVE_SEEN" */ if (((info & (CAVE_SEEN)) != 0) && ((info & (CAVE_TEMP)) == 0)) { int y, x; /* Location */ y = GRID_Y(g); x = GRID_X(g); /* Handle feeling squares */ if ((cave.info2[y][x] & CAVE2_FEEL) != 0) { cave.feeling_squares++; /* Erase the square so you can't 'resee' it */ cave.info2[y][x] &= ~(CAVE2_FEEL); /* Display feeling if necessary */ if (cave.feeling_squares == FEELING1) Command.display_feeling(true); } cave_note_spot(cave, y, x); cave_light_spot(cave, y, x); } } /* Process "old" grids */ for (i = 0; i < fast_temp_n; i++) { /* Grid */ g = fast_temp_g[i]; /* Get grid info */ //info = fast_cave_info[g]; info = cave.info[GRID_Y(g)][GRID_X(g)]; /* Clear "CAVE_TEMP" flag */ info &= ~(CAVE_TEMP); /* Save cave info */ //fast_cave_info[g] = info; cave.info[GRID_Y(g)][GRID_X(g)] = info; /* Was "CAVE_SEEN", is now not "CAVE_SEEN" */ if ((info & (CAVE_SEEN)) == 0) { int y, x; /* Location */ y = GRID_Y(g); x = GRID_X(g); /* Redraw */ cave_light_spot(cave, y, x); } } /* Save 'view_n' */ view_n = fast_view_n; //Nick: We might want to save these too? If wonky, comment out below view_g = fast_view_g; Misc.temp_g = fast_temp_g; }