Move getpos return values to header
[aNetHack.git] / src / vision.c
blob2dcc85b8b92faa1544a0542c8ebf0e7598157547
1 /* NetHack 3.6 vision.c $NHDT-Date: 1448013598 2015/11/20 09:59:58 $ $NHDT-Branch: master $:$NHDT-Revision: 1.27 $ */
2 /* Copyright (c) Dean Luick, with acknowledgements to Dave Cohrs, 1990. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 /* Circles
8 * ==================================================================*/
11 * These numbers are limit offsets for one quadrant of a circle of a given
12 * radius (the first number of each line) from the source. The number in
13 * the comment is the element number (so pointers can be set up). Each
14 * "circle" has as many elements as its radius+1. The radius is the number
15 * of points away from the source that the limit exists. The radius of the
16 * offset on the same row as the source *is* included so we don't have to
17 * make an extra check. For example, a circle of radius 4 has offsets:
19 * XXX +2
20 * ...X +3
21 * ....X +4
22 * ....X +4
23 * @...X +4
26 char circle_data[] = {
27 /* 0*/ 1, 1,
28 /* 2*/ 2, 2, 1,
29 /* 5*/ 3, 3, 2, 1,
30 /* 9*/ 4, 4, 4, 3, 2,
31 /* 14*/ 5, 5, 5, 4, 3, 2,
32 /* 20*/ 6, 6, 6, 5, 5, 4, 2,
33 /* 27*/ 7, 7, 7, 6, 6, 5, 4, 2,
34 /* 35*/ 8, 8, 8, 7, 7, 6, 6, 4, 2,
35 /* 44*/ 9, 9, 9, 9, 8, 8, 7, 6, 5, 3,
36 /* 54*/ 10, 10, 10, 10, 9, 9, 8, 7, 6, 5, 3,
37 /* 65*/ 11, 11, 11, 11, 10, 10, 9, 9, 8, 7, 5, 3,
38 /* 77*/ 12, 12, 12, 12, 11, 11, 10, 10, 9, 8, 7, 5, 3,
39 /* 90*/ 13, 13, 13, 13, 12, 12, 12, 11, 10, 10, 9, 7, 6, 3,
40 /*104*/ 14, 14, 14, 14, 13, 13, 13, 12, 12, 11, 10, 9, 8, 6, 3,
41 /*119*/ 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 11, 10, 9, 8, 6, 3,
42 /*135*/ 16 /* MAX_RADIUS+1; used to terminate range loops -dlc */
46 * These are the starting indexes into the circle_data[] array for a
47 * circle of a given radius.
49 char circle_start[] = {
50 /* */ 0, /* circles of radius zero are not used */
51 /* 1*/ 0,
52 /* 2*/ 2,
53 /* 3*/ 5,
54 /* 4*/ 9,
55 /* 5*/ 14,
56 /* 6*/ 20,
57 /* 7*/ 27,
58 /* 8*/ 35,
59 /* 9*/ 44,
60 /*10*/ 54,
61 /*11*/ 65,
62 /*12*/ 77,
63 /*13*/ 90,
64 /*14*/ 104,
65 /*15*/ 119,
68 /*==========================================================================*/
69 /* Vision (arbitrary line of sight)
70 * =========================================*/
72 /*------ global variables ------*/
74 #if 0 /* (moved to decl.c) */
75 /* True if we need to run a full vision recalculation. */
76 boolean vision_full_recalc = 0;
78 /* Pointers to the current vision array. */
79 char **viz_array;
80 #endif
81 char *viz_rmin, *viz_rmax; /* current vision cs bounds */
83 /*------ local variables ------*/
85 static char could_see[2][ROWNO][COLNO]; /* vision work space */
86 static char *cs_rows0[ROWNO], *cs_rows1[ROWNO];
87 static char cs_rmin0[ROWNO], cs_rmax0[ROWNO];
88 static char cs_rmin1[ROWNO], cs_rmax1[ROWNO];
90 static char viz_clear[ROWNO][COLNO]; /* vision clear/blocked map */
91 static char *viz_clear_rows[ROWNO];
93 static char left_ptrs[ROWNO][COLNO]; /* LOS algorithm helpers */
94 static char right_ptrs[ROWNO][COLNO];
96 /* Forward declarations. */
97 STATIC_DCL void FDECL(fill_point, (int, int));
98 STATIC_DCL void FDECL(dig_point, (int, int));
99 STATIC_DCL void NDECL(view_init);
100 STATIC_DCL void FDECL(view_from, (int, int, char **, char *, char *, int,
101 void (*)(int, int, genericptr_t),
102 genericptr_t));
103 STATIC_DCL void FDECL(get_unused_cs, (char ***, char **, char **));
104 STATIC_DCL void FDECL(rogue_vision, (char **, char *, char *));
106 /* Macro definitions that I can't find anywhere. */
107 #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
108 #define v_abs(z) ((z) < 0 ? -(z) : (z)) /* don't use abs -- it may exist */
111 * vision_init()
113 * The one-time vision initialization routine.
115 * This must be called before mklev() is called in newgame() [allmain.c],
116 * or before a game restore. Else we die a horrible death.
118 void
119 vision_init()
121 int i;
123 /* Set up the pointers. */
124 for (i = 0; i < ROWNO; i++) {
125 cs_rows0[i] = could_see[0][i];
126 cs_rows1[i] = could_see[1][i];
127 viz_clear_rows[i] = viz_clear[i];
130 /* Start out with cs0 as our current array */
131 viz_array = cs_rows0;
132 viz_rmin = cs_rmin0;
133 viz_rmax = cs_rmax0;
135 vision_full_recalc = 0;
136 (void) memset((genericptr_t) could_see, 0, sizeof(could_see));
138 /* Initialize the vision algorithm (currently C or D). */
139 view_init();
141 #ifdef VISION_TABLES
142 /* Note: this initializer doesn't do anything except guarantee that
143 * we're linked properly.
145 vis_tab_init();
146 #endif
150 * does_block()
152 * Returns true if the level feature, object, or monster at (x,y) blocks
153 * sight.
156 does_block(x, y, lev)
157 int x, y;
158 register struct rm *lev;
160 struct obj *obj;
161 struct monst *mon;
163 /* Features that block . . */
164 if (IS_ROCK(lev->typ) || lev->typ == TREE
165 || (IS_DOOR(lev->typ)
166 && (lev->doormask & (D_CLOSED | D_LOCKED | D_TRAPPED))))
167 return 1;
169 if (lev->typ == CLOUD || lev->typ == WATER
170 || (lev->typ == MOAT && Underwater))
171 return 1;
173 /* Boulders block light. */
174 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
175 if (obj->otyp == BOULDER)
176 return 1;
178 /* Mimics mimicing a door or boulder or ... block light. */
179 if ((mon = m_at(x, y)) && (!mon->minvis || See_invisible)
180 && is_lightblocker_mappear(mon))
181 return 1;
183 return 0;
187 * vision_reset()
189 * This must be called *after* the levl[][] structure is set with the new
190 * level and the level monsters and objects are in place.
192 void
193 vision_reset()
195 int y;
196 register int x, i, dig_left, block;
197 register struct rm *lev;
199 /* Start out with cs0 as our current array */
200 viz_array = cs_rows0;
201 viz_rmin = cs_rmin0;
202 viz_rmax = cs_rmax0;
204 (void) memset((genericptr_t) could_see, 0, sizeof(could_see));
206 /* Reset the pointers and clear so that we have a "full" dungeon. */
207 (void) memset((genericptr_t) viz_clear, 0, sizeof(viz_clear));
209 /* Dig the level */
210 for (y = 0; y < ROWNO; y++) {
211 dig_left = 0;
212 block = TRUE; /* location (0,y) is always stone; it's !isok() */
213 lev = &levl[1][y];
214 for (x = 1; x < COLNO; x++, lev += ROWNO)
215 if (block != (IS_ROCK(lev->typ) || does_block(x, y, lev))) {
216 if (block) {
217 for (i = dig_left; i < x; i++) {
218 left_ptrs[y][i] = dig_left;
219 right_ptrs[y][i] = x - 1;
221 } else {
222 i = dig_left;
223 if (dig_left)
224 dig_left--; /* point at first blocked point */
225 for (; i < x; i++) {
226 left_ptrs[y][i] = dig_left;
227 right_ptrs[y][i] = x;
228 viz_clear[y][i] = 1;
231 dig_left = x;
232 block = !block;
234 /* handle right boundary; almost identical for blocked/unblocked */
235 i = dig_left;
236 if (!block && dig_left)
237 dig_left--; /* point at first blocked point */
238 for (; i < COLNO; i++) {
239 left_ptrs[y][i] = dig_left;
240 right_ptrs[y][i] = (COLNO - 1);
241 viz_clear[y][i] = !block;
245 iflags.vision_inited = 1; /* vision is ready */
246 vision_full_recalc = 1; /* we want to run vision_recalc() */
250 * get_unused_cs()
252 * Called from vision_recalc() and at least one light routine. Get pointers
253 * to the unused vision work area.
255 STATIC_OVL void
256 get_unused_cs(rows, rmin, rmax)
257 char ***rows;
258 char **rmin, **rmax;
260 register int row;
261 register char *nrmin, *nrmax;
263 if (viz_array == cs_rows0) {
264 *rows = cs_rows1;
265 *rmin = cs_rmin1;
266 *rmax = cs_rmax1;
267 } else {
268 *rows = cs_rows0;
269 *rmin = cs_rmin0;
270 *rmax = cs_rmax0;
273 /* return an initialized, unused work area */
274 nrmin = *rmin;
275 nrmax = *rmax;
277 (void) memset((genericptr_t) * *rows, 0,
278 ROWNO * COLNO); /* we see nothing */
279 for (row = 0; row < ROWNO; row++) { /* set row min & max */
280 *nrmin++ = COLNO - 1;
281 *nrmax++ = 0;
286 * rogue_vision()
288 * Set the "could see" and in sight bits so vision acts just like the old
289 * rogue game:
291 * + If in a room, the hero can see to the room boundaries.
292 * + The hero can always see adjacent squares.
294 * We set the in_sight bit here as well to escape a bug that shows up
295 * due to the one-sided lit wall hack.
297 STATIC_OVL void
298 rogue_vision(next, rmin, rmax)
299 char **next; /* could_see array pointers */
300 char *rmin, *rmax;
302 int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET; /* no SHARED... */
303 int start, stop, in_door, xhi, xlo, yhi, ylo;
304 register int zx, zy;
306 /* If in a lit room, we are able to see to its boundaries. */
307 /* If dark, set COULD_SEE so various spells work -dlc */
308 if (rnum >= 0) {
309 for (zy = rooms[rnum].ly - 1; zy <= rooms[rnum].hy + 1; zy++) {
310 rmin[zy] = start = rooms[rnum].lx - 1;
311 rmax[zy] = stop = rooms[rnum].hx + 1;
313 for (zx = start; zx <= stop; zx++) {
314 if (rooms[rnum].rlit) {
315 next[zy][zx] = COULD_SEE | IN_SIGHT;
316 levl[zx][zy].seenv = SVALL; /* see the walls */
317 } else
318 next[zy][zx] = COULD_SEE;
323 in_door = levl[u.ux][u.uy].typ == DOOR;
325 /* Can always see adjacent. */
326 ylo = max(u.uy - 1, 0);
327 yhi = min(u.uy + 1, ROWNO - 1);
328 xlo = max(u.ux - 1, 1);
329 xhi = min(u.ux + 1, COLNO - 1);
330 for (zy = ylo; zy <= yhi; zy++) {
331 if (xlo < rmin[zy])
332 rmin[zy] = xlo;
333 if (xhi > rmax[zy])
334 rmax[zy] = xhi;
336 for (zx = xlo; zx <= xhi; zx++) {
337 next[zy][zx] = COULD_SEE | IN_SIGHT;
339 * Yuck, update adjacent non-diagonal positions when in a doorway.
340 * We need to do this to catch the case when we first step into
341 * a room. The room's walls were not seen from the outside, but
342 * now are seen (the seen bits are set just above). However, the
343 * positions are not updated because they were already in sight.
344 * So, we have to do it here.
346 if (in_door && (zx == u.ux || zy == u.uy))
347 newsym(zx, zy);
352 /*#define EXTEND_SPINE*/ /* possibly better looking wall-angle */
354 #ifdef EXTEND_SPINE
356 STATIC_DCL int FDECL(new_angle, (struct rm *, unsigned char *, int, int));
358 * new_angle()
360 * Return the new angle seen by the hero for this location. The angle
361 * bit is given in the value pointed at by sv.
363 * For T walls and crosswall, just setting the angle bit, even though
364 * it is technically correct, doesn't look good. If we can see the
365 * next position beyond the current one and it is a wall that we can
366 * see, then we want to extend a spine of the T to connect with the wall
367 * that is beyond. Example:
369 * Correct, but ugly Extend T spine
371 * | ... | ...
372 * | ... <-- wall beyond & floor --> | ...
373 * | ... | ...
374 * Unseen --> ... | ...
375 * spine +-... <-- trwall & doorway --> +-...
376 * | ... | ...
379 * @ <-- hero --> @
382 * We fake the above check by only checking if the horizontal &
383 * vertical positions adjacent to the crosswall and T wall are
384 * unblocked. Then, _in general_ we can see beyond. Generally,
385 * this is good enough.
387 * + When this function is called we don't have all of the seen
388 * information (we're doing a top down scan in vision_recalc).
389 * We would need to scan once to set all IN_SIGHT and COULD_SEE
390 * bits, then again to correctly set the seenv bits.
391 * + I'm trying to make this as cheap as possible. The display &
392 * vision eat up too much CPU time.
395 * Note: Even as I write this, I'm still not convinced. There are too
396 * many exceptions. I may have to bite the bullet and do more
397 * checks. - Dean 2/11/93
399 STATIC_OVL int
400 new_angle(lev, sv, row, col)
401 struct rm *lev;
402 unsigned char *sv;
403 int row, col;
405 register int res = *sv;
408 * Do extra checks for crosswalls and T walls if we see them from
409 * an angle.
411 if (lev->typ >= CROSSWALL && lev->typ <= TRWALL) {
412 switch (res) {
413 case SV0:
414 if (col > 0 && viz_clear[row][col - 1])
415 res |= SV7;
416 if (row > 0 && viz_clear[row - 1][col])
417 res |= SV1;
418 break;
419 case SV2:
420 if (row > 0 && viz_clear[row - 1][col])
421 res |= SV1;
422 if (col < COLNO - 1 && viz_clear[row][col + 1])
423 res |= SV3;
424 break;
425 case SV4:
426 if (col < COLNO - 1 && viz_clear[row][col + 1])
427 res |= SV3;
428 if (row < ROWNO - 1 && viz_clear[row + 1][col])
429 res |= SV5;
430 break;
431 case SV6:
432 if (row < ROWNO - 1 && viz_clear[row + 1][col])
433 res |= SV5;
434 if (col > 0 && viz_clear[row][col - 1])
435 res |= SV7;
436 break;
439 return res;
441 #else
443 * new_angle()
445 * Return the new angle seen by the hero for this location. The angle
446 * bit is given in the value pointed at by sv.
448 * The other parameters are not used.
450 #define new_angle(lev, sv, row, col) (*sv)
452 #endif
455 * vision_recalc()
457 * Do all of the heavy vision work. Recalculate all locations that could
458 * possibly be seen by the hero --- if the location were lit, etc. Note
459 * which locations are actually seen because of lighting. Then add to
460 * this all locations that be seen by hero due to night vision and x-ray
461 * vision. Finally, compare with what the hero was able to see previously.
462 * Update the difference.
464 * This function is usually called only when the variable 'vision_full_recalc'
465 * is set. The following is a list of places where this function is called,
466 * with three valid values for the control flag parameter:
468 * Control flag = 0. A complete vision recalculation. Generate the vision
469 * tables from scratch. This is necessary to correctly set what the hero
470 * can see. (1) and (2) call this routine for synchronization purposes, (3)
471 * calls this routine so it can operate correctly.
473 * + After the monster move, before input from the player. [moveloop()]
474 * + At end of moveloop. [moveloop() ??? not sure why this is here]
475 * + Right before something is printed. [pline()]
476 * + Right before we do a vision based operation. [do_clear_area()]
477 * + screen redraw, so we can renew all positions in sight. [docrt()]
478 * + When toggling temporary blindness, in case additional events
479 * impacted by vision occur during the same move [make_blinded()]
481 * Control flag = 1. An adjacent vision recalculation. The hero has moved
482 * one square. Knowing this, it might be possible to optimize the vision
483 * recalculation using the current knowledge. This is presently unimplemented
484 * and is treated as a control = 0 call.
486 * + Right after the hero moves. [domove()]
488 * Control flag = 2. Turn off the vision system. Nothing new will be
489 * displayed, since nothing is seen. This is usually done when you need
490 * a newsym() run on all locations in sight, or on some locations but you
491 * don't know which ones.
493 * + Before a screen redraw, so all positions are renewed. [docrt()]
494 * + Right before the hero arrives on a new level. [goto_level()]
495 * + Right after a scroll of light is read. [litroom()]
496 * + After an option has changed that affects vision [parseoptions()]
497 * + Right after the hero is swallowed. [gulpmu()]
498 * + Just before bubbles are moved. [movebubbles()]
500 void
501 vision_recalc(control)
502 int control;
504 char **temp_array; /* points to the old vision array */
505 char **next_array; /* points to the new vision array */
506 char *next_row; /* row pointer for the new array */
507 char *old_row; /* row pointer for the old array */
508 char *next_rmin; /* min pointer for the new array */
509 char *next_rmax; /* max pointer for the new array */
510 char *ranges; /* circle ranges -- used for xray & night vision */
511 int row = 0; /* row counter (outer loop) */
512 int start, stop; /* inner loop starting/stopping index */
513 int dx, dy; /* one step from a lit door or lit wall (see below) */
514 register int col; /* inner loop counter */
515 register struct rm *lev; /* pointer to current pos */
516 struct rm *flev; /* pointer to position in "front" of current pos */
517 extern unsigned char seenv_matrix[3][3]; /* from display.c */
518 static unsigned char colbump[COLNO + 1]; /* cols to bump sv */
519 unsigned char *sv; /* ptr to seen angle bits */
520 int oldseenv; /* previous seenv value */
522 vision_full_recalc = 0; /* reset flag */
523 if (in_mklev || !iflags.vision_inited)
524 return;
527 * Either the light sources have been taken care of, or we must
528 * recalculate them here.
531 /* Get the unused could see, row min, and row max arrays. */
532 get_unused_cs(&next_array, &next_rmin, &next_rmax);
534 /* You see nothing, nothing can see you --- if swallowed or refreshing. */
535 if (u.uswallow || control == 2) {
536 /* do nothing -- get_unused_cs() nulls out the new work area */
538 } else if (Blind) {
540 * Calculate the could_see array even when blind so that monsters
541 * can see you, even if you can't see them. Note that the current
542 * setup allows:
544 * + Monsters to see with the "new" vision, even on the rogue
545 * level.
547 * + Monsters can see you even when you're in a pit.
549 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax, 0,
550 (void FDECL((*), (int, int, genericptr_t))) 0,
551 (genericptr_t) 0);
554 * Our own version of the update loop below. We know we can't see
555 * anything, so we only need update positions we used to be able
556 * to see.
558 temp_array = viz_array; /* set viz_array so newsym() will work */
559 viz_array = next_array;
561 for (row = 0; row < ROWNO; row++) {
562 old_row = temp_array[row];
564 /* Find the min and max positions on the row. */
565 start = min(viz_rmin[row], next_rmin[row]);
566 stop = max(viz_rmax[row], next_rmax[row]);
568 for (col = start; col <= stop; col++)
569 if (old_row[col] & IN_SIGHT)
570 newsym(col, row);
573 /* skip the normal update loop */
574 goto skip;
575 } else if (Is_rogue_level(&u.uz)) {
576 rogue_vision(next_array, next_rmin, next_rmax);
577 } else {
578 int has_night_vision = 1; /* hero has night vision */
580 if (Underwater && !Is_waterlevel(&u.uz)) {
582 * The hero is under water. Only see surrounding locations if
583 * they are also underwater. This overrides night vision but
584 * does not override x-ray vision.
586 has_night_vision = 0;
588 for (row = u.uy - 1; row <= u.uy + 1; row++)
589 for (col = u.ux - 1; col <= u.ux + 1; col++) {
590 if (!isok(col, row) || !is_pool(col, row))
591 continue;
593 next_rmin[row] = min(next_rmin[row], col);
594 next_rmax[row] = max(next_rmax[row], col);
595 next_array[row][col] = IN_SIGHT | COULD_SEE;
598 /* if in a pit, just update for immediate locations */
599 } else if (u.utrap && u.utraptype == TT_PIT) {
600 for (row = u.uy - 1; row <= u.uy + 1; row++) {
601 if (row < 0)
602 continue;
603 if (row >= ROWNO)
604 break;
606 next_rmin[row] = max(0, u.ux - 1);
607 next_rmax[row] = min(COLNO - 1, u.ux + 1);
608 next_row = next_array[row];
610 for (col = next_rmin[row]; col <= next_rmax[row]; col++)
611 next_row[col] = IN_SIGHT | COULD_SEE;
613 } else
614 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax, 0,
615 (void FDECL((*), (int, int, genericptr_t))) 0,
616 (genericptr_t) 0);
619 * Set the IN_SIGHT bit for xray and night vision.
621 if (u.xray_range >= 0) {
622 if (u.xray_range) {
623 ranges = circle_ptr(u.xray_range);
625 for (row = u.uy - u.xray_range; row <= u.uy + u.xray_range;
626 row++) {
627 if (row < 0)
628 continue;
629 if (row >= ROWNO)
630 break;
631 dy = v_abs(u.uy - row);
632 next_row = next_array[row];
634 start = max(0, u.ux - ranges[dy]);
635 stop = min(COLNO - 1, u.ux + ranges[dy]);
637 for (col = start; col <= stop; col++) {
638 char old_row_val = next_row[col];
639 next_row[col] |= IN_SIGHT;
640 oldseenv = levl[col][row].seenv;
641 levl[col][row].seenv = SVALL; /* see all! */
642 /* Update if previously not in sight or new angle. */
643 if (!(old_row_val & IN_SIGHT) || oldseenv != SVALL)
644 newsym(col, row);
647 next_rmin[row] = min(start, next_rmin[row]);
648 next_rmax[row] = max(stop, next_rmax[row]);
651 } else { /* range is 0 */
652 next_array[u.uy][u.ux] |= IN_SIGHT;
653 levl[u.ux][u.uy].seenv = SVALL;
654 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
655 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
659 if (has_night_vision && u.xray_range < u.nv_range) {
660 if (!u.nv_range) { /* range is 0 */
661 next_array[u.uy][u.ux] |= IN_SIGHT;
662 levl[u.ux][u.uy].seenv = SVALL;
663 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
664 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
665 } else if (u.nv_range > 0) {
666 ranges = circle_ptr(u.nv_range);
668 for (row = u.uy - u.nv_range; row <= u.uy + u.nv_range;
669 row++) {
670 if (row < 0)
671 continue;
672 if (row >= ROWNO)
673 break;
674 dy = v_abs(u.uy - row);
675 next_row = next_array[row];
677 start = max(0, u.ux - ranges[dy]);
678 stop = min(COLNO - 1, u.ux + ranges[dy]);
680 for (col = start; col <= stop; col++)
681 if (next_row[col])
682 next_row[col] |= IN_SIGHT;
684 next_rmin[row] = min(start, next_rmin[row]);
685 next_rmax[row] = max(stop, next_rmax[row]);
691 /* Set the correct bits for all light sources. */
692 do_light_sources(next_array);
695 * Make the viz_array the new array so that cansee() will work correctly.
697 temp_array = viz_array;
698 viz_array = next_array;
701 * The main update loop. Here we do two things:
703 * + Set the IN_SIGHT bit for places that we could see and are lit.
704 * + Reset changed places.
706 * There is one thing that make deciding what the hero can see
707 * difficult:
709 * 1. Directional lighting. Items that block light create problems.
710 * The worst offenders are doors. Suppose a door to a lit room
711 * is closed. It is lit on one side, but not on the other. How
712 * do you know? You have to check the closest adjacent position.
713 * Even so, that is not entirely correct. But it seems close
714 * enough for now.
716 colbump[u.ux] = colbump[u.ux + 1] = 1;
717 for (row = 0; row < ROWNO; row++) {
718 dy = u.uy - row;
719 dy = sign(dy);
720 next_row = next_array[row];
721 old_row = temp_array[row];
723 /* Find the min and max positions on the row. */
724 start = min(viz_rmin[row], next_rmin[row]);
725 stop = max(viz_rmax[row], next_rmax[row]);
726 lev = &levl[start][row];
728 sv = &seenv_matrix[dy + 1][start < u.ux ? 0 : (start > u.ux ? 2 : 1)];
730 for (col = start; col <= stop;
731 lev += ROWNO, sv += (int) colbump[++col]) {
732 if (next_row[col] & IN_SIGHT) {
734 * We see this position because of night- or xray-vision.
736 oldseenv = lev->seenv;
737 lev->seenv |=
738 new_angle(lev, sv, row, col); /* update seen angle */
740 /* Update pos if previously not in sight or new angle. */
741 if (!(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
742 newsym(col, row);
744 } else if ((next_row[col] & COULD_SEE)
745 && (lev->lit || (next_row[col] & TEMP_LIT))) {
747 * We see this position because it is lit.
749 if ((IS_DOOR(lev->typ) || lev->typ == SDOOR
750 || IS_WALL(lev->typ)) && !viz_clear[row][col]) {
752 * Make sure doors, walls, boulders or mimics don't show
753 * up
754 * at the end of dark hallways. We do this by checking
755 * the adjacent position. If it is lit, then we can see
756 * the door or wall, otherwise we can't.
758 dx = u.ux - col;
759 dx = sign(dx);
760 flev = &(levl[col + dx][row + dy]);
761 if (flev->lit
762 || next_array[row + dy][col + dx] & TEMP_LIT) {
763 next_row[col] |= IN_SIGHT; /* we see it */
765 oldseenv = lev->seenv;
766 lev->seenv |= new_angle(lev, sv, row, col);
768 /* Update pos if previously not in sight or new
769 * angle.*/
770 if (!(old_row[col] & IN_SIGHT)
771 || oldseenv != lev->seenv)
772 newsym(col, row);
773 } else
774 goto not_in_sight; /* we don't see it */
776 } else {
777 next_row[col] |= IN_SIGHT; /* we see it */
779 oldseenv = lev->seenv;
780 lev->seenv |= new_angle(lev, sv, row, col);
782 /* Update pos if previously not in sight or new angle. */
783 if (!(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
784 newsym(col, row);
786 } else if ((next_row[col] & COULD_SEE) && lev->waslit) {
788 * If we make it here, the hero _could see_ the location,
789 * but doesn't see it (location is not lit).
790 * However, the hero _remembers_ it as lit (waslit is true).
791 * The hero can now see that it is not lit, so change waslit
792 * and update the location.
794 lev->waslit = 0; /* remember lit condition */
795 newsym(col, row);
798 * At this point we know that the row position is *not* in normal
799 * sight. That is, the position is could be seen, but is dark
800 * or LOS is just plain blocked.
802 * Update the position if:
803 * o If the old one *was* in sight. We may need to clean up
804 * the glyph -- E.g. darken room spot, etc.
805 * o If we now could see the location (yet the location is not
806 * lit), but previously we couldn't see the location, or vice
807 * versa. Update the spot because there there may be an
808 * infrared monster there.
810 } else {
811 not_in_sight:
812 if ((old_row[col] & IN_SIGHT)
813 || ((next_row[col] & COULD_SEE)
814 ^ (old_row[col] & COULD_SEE)))
815 newsym(col, row);
818 } /* end for col . . */
819 } /* end for row . . */
820 colbump[u.ux] = colbump[u.ux + 1] = 0;
822 skip:
823 /* This newsym() caused a crash delivering msg about failure to open
824 * dungeon file init_dungeons() -> panic() -> done(11) ->
825 * vision_recalc(2) -> newsym() -> crash! u.ux and u.uy are 0 and
826 * program_state.panicking == 1 under those circumstances
828 if (!program_state.panicking)
829 newsym(u.ux, u.uy); /* Make sure the hero shows up! */
831 /* Set the new min and max pointers. */
832 viz_rmin = next_rmin;
833 viz_rmax = next_rmax;
835 recalc_mapseen();
839 * block_point()
841 * Make the location opaque to light.
843 void
844 block_point(x, y)
845 int x, y;
847 fill_point(y, x);
849 /* recalc light sources here? */
852 * We have to do a full vision recalculation if we "could see" the
853 * location. Why? Suppose some monster opened a way so that the
854 * hero could see a lit room. However, the position of the opening
855 * was out of night-vision range of the hero. Suddenly the hero should
856 * see the lit room.
858 if (viz_array[y][x])
859 vision_full_recalc = 1;
863 * unblock_point()
865 * Make the location transparent to light.
867 void
868 unblock_point(x, y)
869 int x, y;
871 dig_point(y, x);
873 /* recalc light sources here? */
875 if (viz_array[y][x])
876 vision_full_recalc = 1;
879 /*==========================================================================*\
881 | Everything below this line uses (y,x) instead of (x,y) --- the |
882 | algorithms are faster if they are less recursive and can scan |
883 | on a row longer. |
885 \*==========================================================================*/
887 /* ======================================================================= *\
888 Left and Right Pointer Updates
889 \* ======================================================================= */
892 * LEFT and RIGHT pointer rules
895 * **NOTE** The rules changed on 4/4/90. This comment reflects the
896 * new rules. The change was so that the stone-wall optimization
897 * would work.
899 * OK, now the tough stuff. We must maintain our left and right
900 * row pointers. The rules are as follows:
902 * Left Pointers:
903 * ______________
905 * + If you are a clear spot, your left will point to the first
906 * stone to your left. If there is none, then point the first
907 * legal position in the row (0).
909 * + If you are a blocked spot, then your left will point to the
910 * left-most blocked spot to your left that is connected to you.
911 * This means that a left-edge (a blocked spot that has an open
912 * spot on its left) will point to itself.
915 * Right Pointers:
916 * ---------------
917 * + If you are a clear spot, your right will point to the first
918 * stone to your right. If there is none, then point the last
919 * legal position in the row (COLNO-1).
921 * + If you are a blocked spot, then your right will point to the
922 * right-most blocked spot to your right that is connected to you.
923 * This means that a right-edge (a blocked spot that has an open
924 * spot on its right) will point to itself.
926 STATIC_OVL void
927 dig_point(row, col)
928 int row, col;
930 int i;
932 if (viz_clear[row][col])
933 return; /* already done */
935 viz_clear[row][col] = 1;
938 * Boundary cases first.
940 if (col == 0) { /* left edge */
941 if (viz_clear[row][1]) {
942 right_ptrs[row][0] = right_ptrs[row][1];
943 } else {
944 right_ptrs[row][0] = 1;
945 for (i = 1; i <= right_ptrs[row][1]; i++)
946 left_ptrs[row][i] = 1;
948 } else if (col == (COLNO - 1)) { /* right edge */
950 if (viz_clear[row][COLNO - 2]) {
951 left_ptrs[row][COLNO - 1] = left_ptrs[row][COLNO - 2];
952 } else {
953 left_ptrs[row][COLNO - 1] = COLNO - 2;
954 for (i = left_ptrs[row][COLNO - 2]; i < COLNO - 1; i++)
955 right_ptrs[row][i] = COLNO - 2;
959 * At this point, we know we aren't on the boundaries.
961 } else if (viz_clear[row][col - 1] && viz_clear[row][col + 1]) {
962 /* Both sides clear */
963 for (i = left_ptrs[row][col - 1]; i <= col; i++) {
964 if (!viz_clear[row][i])
965 continue; /* catch non-end case */
966 right_ptrs[row][i] = right_ptrs[row][col + 1];
968 for (i = col; i <= right_ptrs[row][col + 1]; i++) {
969 if (!viz_clear[row][i])
970 continue; /* catch non-end case */
971 left_ptrs[row][i] = left_ptrs[row][col - 1];
974 } else if (viz_clear[row][col - 1]) {
975 /* Left side clear, right side blocked. */
976 for (i = col + 1; i <= right_ptrs[row][col + 1]; i++)
977 left_ptrs[row][i] = col + 1;
979 for (i = left_ptrs[row][col - 1]; i <= col; i++) {
980 if (!viz_clear[row][i])
981 continue; /* catch non-end case */
982 right_ptrs[row][i] = col + 1;
984 left_ptrs[row][col] = left_ptrs[row][col - 1];
986 } else if (viz_clear[row][col + 1]) {
987 /* Right side clear, left side blocked. */
988 for (i = left_ptrs[row][col - 1]; i < col; i++)
989 right_ptrs[row][i] = col - 1;
991 for (i = col; i <= right_ptrs[row][col + 1]; i++) {
992 if (!viz_clear[row][i])
993 continue; /* catch non-end case */
994 left_ptrs[row][i] = col - 1;
996 right_ptrs[row][col] = right_ptrs[row][col + 1];
998 } else {
999 /* Both sides blocked */
1000 for (i = left_ptrs[row][col - 1]; i < col; i++)
1001 right_ptrs[row][i] = col - 1;
1003 for (i = col + 1; i <= right_ptrs[row][col + 1]; i++)
1004 left_ptrs[row][i] = col + 1;
1006 left_ptrs[row][col] = col - 1;
1007 right_ptrs[row][col] = col + 1;
1011 STATIC_OVL void
1012 fill_point(row, col)
1013 int row, col;
1015 int i;
1017 if (!viz_clear[row][col])
1018 return;
1020 viz_clear[row][col] = 0;
1022 if (col == 0) {
1023 if (viz_clear[row][1]) { /* adjacent is clear */
1024 right_ptrs[row][0] = 0;
1025 } else {
1026 right_ptrs[row][0] = right_ptrs[row][1];
1027 for (i = 1; i <= right_ptrs[row][1]; i++)
1028 left_ptrs[row][i] = 0;
1030 } else if (col == COLNO - 1) {
1031 if (viz_clear[row][COLNO - 2]) { /* adjacent is clear */
1032 left_ptrs[row][COLNO - 1] = COLNO - 1;
1033 } else {
1034 left_ptrs[row][COLNO - 1] = left_ptrs[row][COLNO - 2];
1035 for (i = left_ptrs[row][COLNO - 2]; i < COLNO - 1; i++)
1036 right_ptrs[row][i] = COLNO - 1;
1040 * Else we know that we are not on an edge.
1042 } else if (viz_clear[row][col - 1] && viz_clear[row][col + 1]) {
1043 /* Both sides clear */
1044 for (i = left_ptrs[row][col - 1] + 1; i <= col; i++)
1045 right_ptrs[row][i] = col;
1047 if (!left_ptrs[row][col - 1]) /* catch the end case */
1048 right_ptrs[row][0] = col;
1050 for (i = col; i < right_ptrs[row][col + 1]; i++)
1051 left_ptrs[row][i] = col;
1053 if (right_ptrs[row][col + 1] == COLNO - 1) /* catch the end case */
1054 left_ptrs[row][COLNO - 1] = col;
1056 } else if (viz_clear[row][col - 1]) {
1057 /* Left side clear, right side blocked. */
1058 for (i = col; i <= right_ptrs[row][col + 1]; i++)
1059 left_ptrs[row][i] = col;
1061 for (i = left_ptrs[row][col - 1] + 1; i < col; i++)
1062 right_ptrs[row][i] = col;
1064 if (!left_ptrs[row][col - 1]) /* catch the end case */
1065 right_ptrs[row][i] = col;
1067 right_ptrs[row][col] = right_ptrs[row][col + 1];
1069 } else if (viz_clear[row][col + 1]) {
1070 /* Right side clear, left side blocked. */
1071 for (i = left_ptrs[row][col - 1]; i <= col; i++)
1072 right_ptrs[row][i] = col;
1074 for (i = col + 1; i < right_ptrs[row][col + 1]; i++)
1075 left_ptrs[row][i] = col;
1077 if (right_ptrs[row][col + 1] == COLNO - 1) /* catch the end case */
1078 left_ptrs[row][i] = col;
1080 left_ptrs[row][col] = left_ptrs[row][col - 1];
1082 } else {
1083 /* Both sides blocked */
1084 for (i = left_ptrs[row][col - 1]; i <= col; i++)
1085 right_ptrs[row][i] = right_ptrs[row][col + 1];
1087 for (i = col; i <= right_ptrs[row][col + 1]; i++)
1088 left_ptrs[row][i] = left_ptrs[row][col - 1];
1092 /*==========================================================================*/
1093 /*==========================================================================*/
1094 /* Use either algorithm C or D. See the config.h for more details.
1095 * =========*/
1098 * Variables local to both Algorithms C and D.
1100 static int start_row;
1101 static int start_col;
1102 static int step;
1103 static char **cs_rows;
1104 static char *cs_left;
1105 static char *cs_right;
1107 static void FDECL((*vis_func), (int, int, genericptr_t));
1108 static genericptr_t varg;
1111 * Both Algorithms C and D use the following macros.
1113 * good_row(z) - Return TRUE if the argument is a legal row.
1114 * set_cs(rowp,col) - Set the local could see array.
1115 * set_min(z) - Save the min value of the argument and the current
1116 * row minimum.
1117 * set_max(z) - Save the max value of the argument and the current
1118 * row maximum.
1120 * The last three macros depend on having local pointers row_min, row_max,
1121 * and rowp being set correctly.
1123 #define set_cs(rowp, col) (rowp[col] = COULD_SEE)
1124 #define good_row(z) ((z) >= 0 && (z) < ROWNO)
1125 #define set_min(z) \
1126 if (*row_min > (z)) \
1127 *row_min = (z)
1128 #define set_max(z) \
1129 if (*row_max < (z)) \
1130 *row_max = (z)
1131 #define is_clear(row, col) viz_clear_rows[row][col]
1134 * clear_path() expanded into 4 macros/functions:
1136 * q1_path()
1137 * q2_path()
1138 * q3_path()
1139 * q4_path()
1141 * "Draw" a line from the start to the given location. Stop if we hit
1142 * something that blocks light. The start and finish points themselves are
1143 * not checked, just the points between them. These routines do _not_
1144 * expect to be called with the same starting and stopping point.
1146 * These routines use the generalized integer Bresenham's algorithm (fast
1147 * line drawing) for all quadrants. The algorithm was taken from _Procedural
1148 * Elements for Computer Graphics_, by David F. Rogers. McGraw-Hill, 1985.
1150 #ifdef MACRO_CPATH /* quadrant calls are macros */
1153 * When called, the result is in "result".
1154 * The first two arguments (srow,scol) are one end of the path. The next
1155 * two arguments (row,col) are the destination. The last argument is
1156 * used as a C language label. This means that it must be different
1157 * in each pair of calls.
1161 * Quadrant I (step < 0).
1163 #define q1_path(srow, scol, y2, x2, label) \
1165 int dx, dy; \
1166 register int k, err, x, y, dxs, dys; \
1168 x = (scol); \
1169 y = (srow); \
1170 dx = (x2) -x; \
1171 dy = y - (y2); \
1173 result = 0; /* default to a blocked path */ \
1175 dxs = dx << 1; /* save the shifted values */ \
1176 dys = dy << 1; \
1177 if (dy > dx) { \
1178 err = dxs - dy; \
1180 for (k = dy - 1; k; k--) { \
1181 if (err >= 0) { \
1182 x++; \
1183 err -= dys; \
1185 y--; \
1186 err += dxs; \
1187 if (!is_clear(y, x)) \
1188 goto label; /* blocked */ \
1190 } else { \
1191 err = dys - dx; \
1193 for (k = dx - 1; k; k--) { \
1194 if (err >= 0) { \
1195 y--; \
1196 err -= dxs; \
1198 x++; \
1199 err += dys; \
1200 if (!is_clear(y, x)) \
1201 goto label; /* blocked */ \
1205 result = 1; \
1209 * Quadrant IV (step > 0).
1211 #define q4_path(srow, scol, y2, x2, label) \
1213 int dx, dy; \
1214 register int k, err, x, y, dxs, dys; \
1216 x = (scol); \
1217 y = (srow); \
1218 dx = (x2) -x; \
1219 dy = (y2) -y; \
1221 result = 0; /* default to a blocked path */ \
1223 dxs = dx << 1; /* save the shifted values */ \
1224 dys = dy << 1; \
1225 if (dy > dx) { \
1226 err = dxs - dy; \
1228 for (k = dy - 1; k; k--) { \
1229 if (err >= 0) { \
1230 x++; \
1231 err -= dys; \
1233 y++; \
1234 err += dxs; \
1235 if (!is_clear(y, x)) \
1236 goto label; /* blocked */ \
1239 } else { \
1240 err = dys - dx; \
1242 for (k = dx - 1; k; k--) { \
1243 if (err >= 0) { \
1244 y++; \
1245 err -= dxs; \
1247 x++; \
1248 err += dys; \
1249 if (!is_clear(y, x)) \
1250 goto label; /* blocked */ \
1254 result = 1; \
1258 * Quadrant II (step < 0).
1260 #define q2_path(srow, scol, y2, x2, label) \
1262 int dx, dy; \
1263 register int k, err, x, y, dxs, dys; \
1265 x = (scol); \
1266 y = (srow); \
1267 dx = x - (x2); \
1268 dy = y - (y2); \
1270 result = 0; /* default to a blocked path */ \
1272 dxs = dx << 1; /* save the shifted values */ \
1273 dys = dy << 1; \
1274 if (dy > dx) { \
1275 err = dxs - dy; \
1277 for (k = dy - 1; k; k--) { \
1278 if (err >= 0) { \
1279 x--; \
1280 err -= dys; \
1282 y--; \
1283 err += dxs; \
1284 if (!is_clear(y, x)) \
1285 goto label; /* blocked */ \
1287 } else { \
1288 err = dys - dx; \
1290 for (k = dx - 1; k; k--) { \
1291 if (err >= 0) { \
1292 y--; \
1293 err -= dxs; \
1295 x--; \
1296 err += dys; \
1297 if (!is_clear(y, x)) \
1298 goto label; /* blocked */ \
1302 result = 1; \
1306 * Quadrant III (step > 0).
1308 #define q3_path(srow, scol, y2, x2, label) \
1310 int dx, dy; \
1311 register int k, err, x, y, dxs, dys; \
1313 x = (scol); \
1314 y = (srow); \
1315 dx = x - (x2); \
1316 dy = (y2) -y; \
1318 result = 0; /* default to a blocked path */ \
1320 dxs = dx << 1; /* save the shifted values */ \
1321 dys = dy << 1; \
1322 if (dy > dx) { \
1323 err = dxs - dy; \
1325 for (k = dy - 1; k; k--) { \
1326 if (err >= 0) { \
1327 x--; \
1328 err -= dys; \
1330 y++; \
1331 err += dxs; \
1332 if (!is_clear(y, x)) \
1333 goto label; /* blocked */ \
1336 } else { \
1337 err = dys - dx; \
1339 for (k = dx - 1; k; k--) { \
1340 if (err >= 0) { \
1341 y++; \
1342 err -= dxs; \
1344 x--; \
1345 err += dys; \
1346 if (!is_clear(y, x)) \
1347 goto label; /* blocked */ \
1351 result = 1; \
1354 #else /* !MACRO_CPATH -- quadrants are really functions */
1356 STATIC_DCL int FDECL(_q1_path, (int, int, int, int));
1357 STATIC_DCL int FDECL(_q2_path, (int, int, int, int));
1358 STATIC_DCL int FDECL(_q3_path, (int, int, int, int));
1359 STATIC_DCL int FDECL(_q4_path, (int, int, int, int));
1361 #define q1_path(sy, sx, y, x, dummy) result = _q1_path(sy, sx, y, x)
1362 #define q2_path(sy, sx, y, x, dummy) result = _q2_path(sy, sx, y, x)
1363 #define q3_path(sy, sx, y, x, dummy) result = _q3_path(sy, sx, y, x)
1364 #define q4_path(sy, sx, y, x, dummy) result = _q4_path(sy, sx, y, x)
1367 * Quadrant I (step < 0).
1369 STATIC_OVL int
1370 _q1_path(srow, scol, y2, x2)
1371 int scol, srow, y2, x2;
1373 int dx, dy;
1374 register int k, err, x, y, dxs, dys;
1376 x = scol;
1377 y = srow;
1378 dx = x2 - x;
1379 dy = y - y2;
1381 dxs = dx << 1; /* save the shifted values */
1382 dys = dy << 1;
1383 if (dy > dx) {
1384 err = dxs - dy;
1386 for (k = dy - 1; k; k--) {
1387 if (err >= 0) {
1388 x++;
1389 err -= dys;
1391 y--;
1392 err += dxs;
1393 if (!is_clear(y, x))
1394 return 0; /* blocked */
1396 } else {
1397 err = dys - dx;
1399 for (k = dx - 1; k; k--) {
1400 if (err >= 0) {
1401 y--;
1402 err -= dxs;
1404 x++;
1405 err += dys;
1406 if (!is_clear(y, x))
1407 return 0; /* blocked */
1411 return 1;
1415 * Quadrant IV (step > 0).
1417 STATIC_OVL int
1418 _q4_path(srow, scol, y2, x2)
1419 int scol, srow, y2, x2;
1421 int dx, dy;
1422 register int k, err, x, y, dxs, dys;
1424 x = scol;
1425 y = srow;
1426 dx = x2 - x;
1427 dy = y2 - y;
1429 dxs = dx << 1; /* save the shifted values */
1430 dys = dy << 1;
1431 if (dy > dx) {
1432 err = dxs - dy;
1434 for (k = dy - 1; k; k--) {
1435 if (err >= 0) {
1436 x++;
1437 err -= dys;
1439 y++;
1440 err += dxs;
1441 if (!is_clear(y, x))
1442 return 0; /* blocked */
1444 } else {
1445 err = dys - dx;
1447 for (k = dx - 1; k; k--) {
1448 if (err >= 0) {
1449 y++;
1450 err -= dxs;
1452 x++;
1453 err += dys;
1454 if (!is_clear(y, x))
1455 return 0; /* blocked */
1459 return 1;
1463 * Quadrant II (step < 0).
1465 STATIC_OVL int
1466 _q2_path(srow, scol, y2, x2)
1467 int scol, srow, y2, x2;
1469 int dx, dy;
1470 register int k, err, x, y, dxs, dys;
1472 x = scol;
1473 y = srow;
1474 dx = x - x2;
1475 dy = y - y2;
1477 dxs = dx << 1; /* save the shifted values */
1478 dys = dy << 1;
1479 if (dy > dx) {
1480 err = dxs - dy;
1482 for (k = dy - 1; k; k--) {
1483 if (err >= 0) {
1484 x--;
1485 err -= dys;
1487 y--;
1488 err += dxs;
1489 if (!is_clear(y, x))
1490 return 0; /* blocked */
1492 } else {
1493 err = dys - dx;
1495 for (k = dx - 1; k; k--) {
1496 if (err >= 0) {
1497 y--;
1498 err -= dxs;
1500 x--;
1501 err += dys;
1502 if (!is_clear(y, x))
1503 return 0; /* blocked */
1507 return 1;
1511 * Quadrant III (step > 0).
1513 STATIC_OVL int
1514 _q3_path(srow, scol, y2, x2)
1515 int scol, srow, y2, x2;
1517 int dx, dy;
1518 register int k, err, x, y, dxs, dys;
1520 x = scol;
1521 y = srow;
1522 dx = x - x2;
1523 dy = y2 - y;
1525 dxs = dx << 1; /* save the shifted values */
1526 dys = dy << 1;
1527 if (dy > dx) {
1528 err = dxs - dy;
1530 for (k = dy - 1; k; k--) {
1531 if (err >= 0) {
1532 x--;
1533 err -= dys;
1535 y++;
1536 err += dxs;
1537 if (!is_clear(y, x))
1538 return 0; /* blocked */
1540 } else {
1541 err = dys - dx;
1543 for (k = dx - 1; k; k--) {
1544 if (err >= 0) {
1545 y++;
1546 err -= dxs;
1548 x--;
1549 err += dys;
1550 if (!is_clear(y, x))
1551 return 0; /* blocked */
1555 return 1;
1558 #endif /* ?MACRO_CPATH */
1561 * Use vision tables to determine if there is a clear path from
1562 * (col1,row1) to (col2,row2). This is used by:
1563 * m_cansee()
1564 * m_canseeu()
1565 * do_light_sources()
1567 boolean
1568 clear_path(col1, row1, col2, row2)
1569 int col1, row1, col2, row2;
1571 int result;
1573 if (col1 < col2) {
1574 if (row1 > row2) {
1575 q1_path(row1, col1, row2, col2, cleardone);
1576 } else {
1577 q4_path(row1, col1, row2, col2, cleardone);
1579 } else {
1580 if (row1 > row2) {
1581 q2_path(row1, col1, row2, col2, cleardone);
1582 } else if (row1 == row2 && col1 == col2) {
1583 result = 1;
1584 } else {
1585 q3_path(row1, col1, row2, col2, cleardone);
1588 #ifdef MACRO_CPATH
1589 cleardone:
1590 #endif
1591 return (boolean) result;
1594 #ifdef VISION_TABLES
1595 /*==========================================================================*\
1596 GENERAL LINE OF SIGHT
1597 Algorithm D
1598 \*==========================================================================*/
1601 * Indicate caller for the shadow routines.
1603 #define FROM_RIGHT 0
1604 #define FROM_LEFT 1
1607 * Include the table definitions.
1609 #include "vis_tab.h"
1611 /* 3D table pointers. */
1612 static close2d *close_dy[CLOSE_MAX_BC_DY];
1613 static far2d *far_dy[FAR_MAX_BC_DY];
1615 STATIC_DCL void FDECL(right_side, (int, int, int, int, int,
1616 int, int, char *));
1617 STATIC_DCL void FDECL(left_side, (int, int, int, int, int, int, int, char *));
1618 STATIC_DCL int FDECL(close_shadow, (int, int, int, int));
1619 STATIC_DCL int FDECL(far_shadow, (int, int, int, int));
1622 * Initialize algorithm D's table pointers. If we don't have these,
1623 * then we do 3D table lookups. Verrrry slow.
1625 STATIC_OVL void
1626 view_init()
1628 int i;
1630 for (i = 0; i < CLOSE_MAX_BC_DY; i++)
1631 close_dy[i] = &close_table[i];
1633 for (i = 0; i < FAR_MAX_BC_DY; i++)
1634 far_dy[i] = &far_table[i];
1638 * If the far table has an entry of OFF_TABLE, then the far block prevents
1639 * us from seeing the location just above/below it. I.e. the first visible
1640 * location is one *before* the block.
1642 #define OFF_TABLE 0xff
1644 STATIC_OVL int
1645 close_shadow(side, this_row, block_row, block_col)
1646 int side, this_row, block_row, block_col;
1648 register int sdy, sdx, pdy, offset;
1651 * If on the same column (block_row = -1), then we can see it.
1653 if (block_row < 0)
1654 return block_col;
1656 /* Take explicit absolute values. Adjust. */
1657 if ((sdy = (start_row - block_row)) < 0)
1658 sdy = -sdy;
1659 --sdy; /* src dy */
1660 if ((sdx = (start_col - block_col)) < 0)
1661 sdx = -sdx; /* src dx */
1662 if ((pdy = (block_row - this_row)) < 0)
1663 pdy = -pdy; /* point dy */
1665 if (sdy < 0 || sdy >= CLOSE_MAX_SB_DY || sdx >= CLOSE_MAX_SB_DX
1666 || pdy >= CLOSE_MAX_BC_DY) {
1667 impossible("close_shadow: bad value");
1668 return block_col;
1670 offset = close_dy[sdy]->close[sdx][pdy];
1671 if (side == FROM_RIGHT)
1672 return block_col + offset;
1674 return block_col - offset;
1677 STATIC_OVL int
1678 far_shadow(side, this_row, block_row, block_col)
1679 int side, this_row, block_row, block_col;
1681 register int sdy, sdx, pdy, offset;
1684 * Take care of a bug that shows up only on the borders.
1686 * If the block is beyond the border, then the row is negative. Return
1687 * the block's column number (should be 0 or COLNO-1).
1689 * Could easily have the column be -1, but then wouldn't know if it was
1690 * the left or right border.
1692 if (block_row < 0)
1693 return block_col;
1695 /* Take explicit absolute values. Adjust. */
1696 if ((sdy = (start_row - block_row)) < 0)
1697 sdy = -sdy; /* src dy */
1698 if ((sdx = (start_col - block_col)) < 0)
1699 sdx = -sdx;
1700 --sdx; /* src dx */
1701 if ((pdy = (block_row - this_row)) < 0)
1702 pdy = -pdy;
1703 --pdy; /* point dy */
1705 if (sdy >= FAR_MAX_SB_DY || sdx < 0 || sdx >= FAR_MAX_SB_DX || pdy < 0
1706 || pdy >= FAR_MAX_BC_DY) {
1707 impossible("far_shadow: bad value");
1708 return block_col;
1710 if ((offset = far_dy[sdy]->far_q[sdx][pdy]) == OFF_TABLE)
1711 offset = -1;
1712 if (side == FROM_RIGHT)
1713 return block_col + offset;
1715 return block_col - offset;
1719 * right_side()
1721 * Figure out what could be seen on the right side of the source.
1723 STATIC_OVL void
1724 right_side(row, cb_row, cb_col, fb_row, fb_col, left, right_mark, limits)
1725 int row; /* current row */
1726 int cb_row, cb_col; /* close block row and col */
1727 int fb_row, fb_col; /* far block row and col */
1728 int left; /* left mark of the previous row */
1729 int right_mark; /* right mark of previous row */
1730 char *limits; /* points at range limit for current row, or NULL */
1732 register int i;
1733 register char *rowp = NULL;
1734 int hit_stone = 0;
1735 int left_shadow, right_shadow, loc_right;
1736 int lblock_col; /* local block column (current row) */
1737 int nrow, deeper;
1738 char *row_min = NULL; /* left most */
1739 char *row_max = NULL; /* right most */
1740 int lim_max; /* right most limit of circle */
1742 nrow = row + step;
1743 deeper = good_row(nrow) && (!limits || (*limits >= *(limits + 1)));
1744 if (!vis_func) {
1745 rowp = cs_rows[row];
1746 row_min = &cs_left[row];
1747 row_max = &cs_right[row];
1749 if (limits) {
1750 lim_max = start_col + *limits;
1751 if (lim_max > COLNO - 1)
1752 lim_max = COLNO - 1;
1753 if (right_mark > lim_max)
1754 right_mark = lim_max;
1755 limits++; /* prepare for next row */
1756 } else
1757 lim_max = COLNO - 1;
1760 * Get the left shadow from the close block. This value could be
1761 * illegal.
1763 left_shadow = close_shadow(FROM_RIGHT, row, cb_row, cb_col);
1766 * Mark all stone walls as seen before the left shadow. All this work
1767 * for a special case.
1769 * NOTE. With the addition of this code in here, it is now *required*
1770 * for the algorithm to work correctly. If this is commented out,
1771 * change the above assignment so that left and not left_shadow is the
1772 * variable that gets the shadow.
1774 while (left <= right_mark) {
1775 loc_right = right_ptrs[row][left];
1776 if (loc_right > lim_max)
1777 loc_right = lim_max;
1778 if (viz_clear_rows[row][left]) {
1779 if (loc_right >= left_shadow) {
1780 left = left_shadow; /* opening ends beyond shadow */
1781 break;
1783 left = loc_right;
1784 loc_right = right_ptrs[row][left];
1785 if (loc_right > lim_max)
1786 loc_right = lim_max;
1787 if (left == loc_right)
1788 return; /* boundary */
1790 /* Shadow covers opening, beyond right mark */
1791 if (left == right_mark && left_shadow > right_mark)
1792 return;
1795 if (loc_right > right_mark) /* can't see stone beyond the mark */
1796 loc_right = right_mark;
1798 if (vis_func) {
1799 for (i = left; i <= loc_right; i++)
1800 (*vis_func)(i, row, varg);
1801 } else {
1802 for (i = left; i <= loc_right; i++)
1803 set_cs(rowp, i);
1804 set_min(left);
1805 set_max(loc_right);
1808 if (loc_right == right_mark)
1809 return; /* all stone */
1810 if (loc_right >= left_shadow)
1811 hit_stone = 1;
1812 left = loc_right + 1;
1816 * At this point we are at the first visible clear spot on or beyond
1817 * the left shadow, unless the left shadow is an illegal value. If we
1818 * have "hit stone" then we have a stone wall just to our left.
1822 * Get the right shadow. Make sure that it is a legal value.
1824 if ((right_shadow = far_shadow(FROM_RIGHT, row, fb_row, fb_col)) >= COLNO)
1825 right_shadow = COLNO - 1;
1827 * Make vertical walls work the way we want them. In this case, we
1828 * note when the close block blocks the column just above/beneath
1829 * it (right_shadow < fb_col [actually right_shadow == fb_col-1]). If
1830 * the location is filled, then we want to see it, so we put the
1831 * right shadow back (same as fb_col).
1833 if (right_shadow < fb_col && !viz_clear_rows[row][fb_col])
1834 right_shadow = fb_col;
1835 if (right_shadow > lim_max)
1836 right_shadow = lim_max;
1839 * Main loop. Within the range of sight of the previous row, mark all
1840 * stone walls as seen. Follow open areas recursively.
1842 while (left <= right_mark) {
1843 /* Get the far right of the opening or wall */
1844 loc_right = right_ptrs[row][left];
1845 if (loc_right > lim_max)
1846 loc_right = lim_max;
1848 if (!viz_clear_rows[row][left]) {
1849 hit_stone = 1; /* use stone on this row as close block */
1851 * We can see all of the wall until the next open spot or the
1852 * start of the shadow caused by the far block (right).
1854 * Can't see stone beyond the right mark.
1856 if (loc_right > right_mark)
1857 loc_right = right_mark;
1859 if (vis_func) {
1860 for (i = left; i <= loc_right; i++)
1861 (*vis_func)(i, row, varg);
1862 } else {
1863 for (i = left; i <= loc_right; i++)
1864 set_cs(rowp, i);
1865 set_min(left);
1866 set_max(loc_right);
1869 if (loc_right == right_mark)
1870 return; /* hit the end */
1871 left = loc_right + 1;
1872 loc_right = right_ptrs[row][left];
1873 if (loc_right > lim_max)
1874 loc_right = lim_max;
1875 /* fall through... we know at least one position is visible */
1879 * We are in an opening.
1881 * If this is the first open spot since the could see area (this is
1882 * true if we have hit stone), get the shadow generated by the wall
1883 * just to our left.
1885 if (hit_stone) {
1886 lblock_col = left - 1; /* local block column */
1887 left = close_shadow(FROM_RIGHT, row, row, lblock_col);
1888 if (left > lim_max)
1889 break; /* off the end */
1893 * Check if the shadow covers the opening. If it does, then
1894 * move to end of the opening. A shadow generated on from a
1895 * wall on this row does *not* cover the wall on the right
1896 * of the opening.
1898 if (left >= loc_right) {
1899 if (loc_right == lim_max) { /* boundary */
1900 if (left == lim_max) {
1901 if (vis_func)
1902 (*vis_func)(lim_max, row, varg);
1903 else {
1904 set_cs(rowp, lim_max); /* last pos */
1905 set_max(lim_max);
1908 return; /* done */
1910 left = loc_right;
1911 continue;
1915 * If the far wall of the opening (loc_right) is closer than the
1916 * shadow limit imposed by the far block (right) then use the far
1917 * wall as our new far block when we recurse.
1919 * If the limits are the the same, and the far block really exists
1920 * (fb_row >= 0) then do the same as above.
1922 * Normally, the check would be for the far wall being closer OR EQUAL
1923 * to the shadow limit. However, there is a bug that arises from the
1924 * fact that the clear area pointers end in an open space (if it
1925 * exists) on a boundary. This then makes a far block exist where it
1926 * shouldn't --- on a boundary. To get around that, I had to
1927 * introduce the concept of a non-existent far block (when the
1928 * row < 0). Next I have to check for it. Here is where that check
1929 * exists.
1931 if ((loc_right < right_shadow)
1932 || (fb_row >= 0 && loc_right == right_shadow)) {
1933 if (vis_func) {
1934 for (i = left; i <= loc_right; i++)
1935 (*vis_func)(i, row, varg);
1936 } else {
1937 for (i = left; i <= loc_right; i++)
1938 set_cs(rowp, i);
1939 set_min(left);
1940 set_max(loc_right);
1943 if (deeper) {
1944 if (hit_stone)
1945 right_side(nrow, row, lblock_col, row, loc_right, left,
1946 loc_right, limits);
1947 else
1948 right_side(nrow, cb_row, cb_col, row, loc_right, left,
1949 loc_right, limits);
1953 * The following line, setting hit_stone, is needed for those
1954 * walls that are only 1 wide. If hit stone is *not* set and
1955 * the stone is only one wide, then the close block is the old
1956 * one instead one on the current row. A way around having to
1957 * set it here is to make left = loc_right (not loc_right+1) and
1958 * let the outer loop take care of it. However, if we do that
1959 * then we then have to check for boundary conditions here as
1960 * well.
1962 hit_stone = 1;
1964 left = loc_right + 1;
1967 * The opening extends beyond the right mark. This means that
1968 * the next far block is the current far block.
1970 } else {
1971 if (vis_func) {
1972 for (i = left; i <= right_shadow; i++)
1973 (*vis_func)(i, row, varg);
1974 } else {
1975 for (i = left; i <= right_shadow; i++)
1976 set_cs(rowp, i);
1977 set_min(left);
1978 set_max(right_shadow);
1981 if (deeper) {
1982 if (hit_stone)
1983 right_side(nrow, row, lblock_col, fb_row, fb_col, left,
1984 right_shadow, limits);
1985 else
1986 right_side(nrow, cb_row, cb_col, fb_row, fb_col, left,
1987 right_shadow, limits);
1990 return; /* we're outta here */
1996 * left_side()
1998 * This routine is the mirror image of right_side(). Please see right_side()
1999 * for blow by blow comments.
2001 STATIC_OVL void
2002 left_side(row, cb_row, cb_col, fb_row, fb_col, left_mark, right, limits)
2003 int row; /* the current row */
2004 int cb_row, cb_col; /* close block row and col */
2005 int fb_row, fb_col; /* far block row and col */
2006 int left_mark; /* left mark of previous row */
2007 int right; /* right mark of the previous row */
2008 char *limits;
2010 register int i;
2011 register char *rowp = NULL;
2012 int hit_stone = 0;
2013 int left_shadow, right_shadow, loc_left;
2014 int lblock_col; /* local block column (current row) */
2015 int nrow, deeper;
2016 char *row_min = NULL; /* left most */
2017 char *row_max = NULL; /* right most */
2018 int lim_min;
2020 nrow = row + step;
2021 deeper = good_row(nrow) && (!limits || (*limits >= *(limits + 1)));
2022 if (!vis_func) {
2023 rowp = cs_rows[row];
2024 row_min = &cs_left[row];
2025 row_max = &cs_right[row];
2027 if (limits) {
2028 lim_min = start_col - *limits;
2029 if (lim_min < 0)
2030 lim_min = 0;
2031 if (left_mark < lim_min)
2032 left_mark = lim_min;
2033 limits++; /* prepare for next row */
2034 } else
2035 lim_min = 0;
2037 /* This value could be illegal. */
2038 right_shadow = close_shadow(FROM_LEFT, row, cb_row, cb_col);
2040 while (right >= left_mark) {
2041 loc_left = left_ptrs[row][right];
2042 if (loc_left < lim_min)
2043 loc_left = lim_min;
2044 if (viz_clear_rows[row][right]) {
2045 if (loc_left <= right_shadow) {
2046 right = right_shadow; /* opening ends beyond shadow */
2047 break;
2049 right = loc_left;
2050 loc_left = left_ptrs[row][right];
2051 if (loc_left < lim_min)
2052 loc_left = lim_min;
2053 if (right == loc_left)
2054 return; /* boundary */
2057 if (loc_left < left_mark) /* can't see beyond the left mark */
2058 loc_left = left_mark;
2060 if (vis_func) {
2061 for (i = loc_left; i <= right; i++)
2062 (*vis_func)(i, row, varg);
2063 } else {
2064 for (i = loc_left; i <= right; i++)
2065 set_cs(rowp, i);
2066 set_min(loc_left);
2067 set_max(right);
2070 if (loc_left == left_mark)
2071 return; /* all stone */
2072 if (loc_left <= right_shadow)
2073 hit_stone = 1;
2074 right = loc_left - 1;
2077 /* At first visible clear spot on or beyond the right shadow. */
2079 if ((left_shadow = far_shadow(FROM_LEFT, row, fb_row, fb_col)) < 0)
2080 left_shadow = 0;
2082 /* Do vertical walls as we want. */
2083 if (left_shadow > fb_col && !viz_clear_rows[row][fb_col])
2084 left_shadow = fb_col;
2085 if (left_shadow < lim_min)
2086 left_shadow = lim_min;
2088 while (right >= left_mark) {
2089 loc_left = left_ptrs[row][right];
2091 if (!viz_clear_rows[row][right]) {
2092 hit_stone = 1; /* use stone on this row as close block */
2094 /* We can only see walls until the left mark */
2095 if (loc_left < left_mark)
2096 loc_left = left_mark;
2098 if (vis_func) {
2099 for (i = loc_left; i <= right; i++)
2100 (*vis_func)(i, row, varg);
2101 } else {
2102 for (i = loc_left; i <= right; i++)
2103 set_cs(rowp, i);
2104 set_min(loc_left);
2105 set_max(right);
2108 if (loc_left == left_mark)
2109 return; /* hit end */
2110 right = loc_left - 1;
2111 loc_left = left_ptrs[row][right];
2112 if (loc_left < lim_min)
2113 loc_left = lim_min;
2114 /* fall through...*/
2117 /* We are in an opening. */
2118 if (hit_stone) {
2119 lblock_col = right + 1; /* stone block (local) */
2120 right = close_shadow(FROM_LEFT, row, row, lblock_col);
2121 if (right < lim_min)
2122 return; /* off the end */
2125 /* Check if the shadow covers the opening. */
2126 if (right <= loc_left) {
2127 /* Make a boundary condition work. */
2128 if (loc_left == lim_min) { /* at boundary */
2129 if (right == lim_min) {
2130 if (vis_func)
2131 (*vis_func)(lim_min, row, varg);
2132 else {
2133 set_cs(rowp, lim_min); /* caught the last pos */
2134 set_min(lim_min);
2137 return; /* and break out the loop */
2140 right = loc_left;
2141 continue;
2144 /* If the far wall of the opening is closer than the shadow limit. */
2145 if ((loc_left > left_shadow)
2146 || (fb_row >= 0 && loc_left == left_shadow)) {
2147 if (vis_func) {
2148 for (i = loc_left; i <= right; i++)
2149 (*vis_func)(i, row, varg);
2150 } else {
2151 for (i = loc_left; i <= right; i++)
2152 set_cs(rowp, i);
2153 set_min(loc_left);
2154 set_max(right);
2157 if (deeper) {
2158 if (hit_stone)
2159 left_side(nrow, row, lblock_col, row, loc_left, loc_left,
2160 right, limits);
2161 else
2162 left_side(nrow, cb_row, cb_col, row, loc_left, loc_left,
2163 right, limits);
2166 hit_stone = 1; /* needed for walls of width 1 */
2167 right = loc_left - 1;
2169 /* The opening extends beyond the left mark. */
2170 } else {
2171 if (vis_func) {
2172 for (i = left_shadow; i <= right; i++)
2173 (*vis_func)(i, row, varg);
2174 } else {
2175 for (i = left_shadow; i <= right; i++)
2176 set_cs(rowp, i);
2177 set_min(left_shadow);
2178 set_max(right);
2181 if (deeper) {
2182 if (hit_stone)
2183 left_side(nrow, row, lblock_col, fb_row, fb_col,
2184 left_shadow, right, limits);
2185 else
2186 left_side(nrow, cb_row, cb_col, fb_row, fb_col,
2187 left_shadow, right, limits);
2190 return; /* we're outta here */
2196 * view_from
2198 * Calculate a view from the given location. Initialize and fill a
2199 * ROWNOxCOLNO array (could_see) with all the locations that could be
2200 * seen from the source location. Initialize and fill the left most
2201 * and right most boundaries of what could be seen.
2203 STATIC_OVL void
2204 view_from(srow, scol, loc_cs_rows, left_most, right_most, range, func, arg)
2205 int srow, scol; /* source row and column */
2206 char **loc_cs_rows; /* could_see array (row pointers) */
2207 char *left_most, *right_most; /* limits of what could be seen */
2208 int range; /* 0 if unlimited */
2209 void FDECL((*func), (int, int, genericptr_t));
2210 genericptr_t arg;
2212 register int i;
2213 char *rowp;
2214 int nrow, left, right, left_row, right_row;
2215 char *limits;
2217 /* Set globals for near_shadow(), far_shadow(), etc. to use. */
2218 start_col = scol;
2219 start_row = srow;
2220 cs_rows = loc_cs_rows;
2221 cs_left = left_most;
2222 cs_right = right_most;
2223 vis_func = func;
2224 varg = arg;
2226 /* Find the left and right limits of sight on the starting row. */
2227 if (viz_clear_rows[srow][scol]) {
2228 left = left_ptrs[srow][scol];
2229 right = right_ptrs[srow][scol];
2230 } else {
2231 left = (!scol) ? 0 : (viz_clear_rows[srow][scol - 1]
2232 ? left_ptrs[srow][scol - 1]
2233 : scol - 1);
2234 right = (scol == COLNO - 1) ? COLNO - 1
2235 : (viz_clear_rows[srow][scol + 1]
2236 ? right_ptrs[srow][scol + 1]
2237 : scol + 1);
2240 if (range) {
2241 if (range > MAX_RADIUS || range < 1)
2242 panic("view_from called with range %d", range);
2243 limits = circle_ptr(range) + 1; /* start at next row */
2244 if (left < scol - range)
2245 left = scol - range;
2246 if (right > scol + range)
2247 right = scol + range;
2248 } else
2249 limits = (char *) 0;
2251 if (func) {
2252 for (i = left; i <= right; i++)
2253 (*func)(i, srow, arg);
2254 } else {
2255 /* Row optimization */
2256 rowp = cs_rows[srow];
2258 /* We know that we can see our row. */
2259 for (i = left; i <= right; i++)
2260 set_cs(rowp, i);
2261 cs_left[srow] = left;
2262 cs_right[srow] = right;
2265 /* The far block has a row number of -1 if we are on an edge. */
2266 right_row = (right == COLNO - 1) ? -1 : srow;
2267 left_row = (!left) ? -1 : srow;
2270 * Check what could be seen in quadrants.
2272 if ((nrow = srow + 1) < ROWNO) {
2273 step = 1; /* move down */
2274 if (scol < COLNO - 1)
2275 right_side(nrow, -1, scol, right_row, right, scol, right, limits);
2276 if (scol)
2277 left_side(nrow, -1, scol, left_row, left, left, scol, limits);
2280 if ((nrow = srow - 1) >= 0) {
2281 step = -1; /* move up */
2282 if (scol < COLNO - 1)
2283 right_side(nrow, -1, scol, right_row, right, scol, right, limits);
2284 if (scol)
2285 left_side(nrow, -1, scol, left_row, left, left, scol, limits);
2289 #else /*===== End of algorithm D =====*/
2291 /*==========================================================================*\
2292 GENERAL LINE OF SIGHT
2293 Algorithm C
2294 \*==========================================================================*/
2297 * Defines local to Algorithm C.
2299 STATIC_DCL void FDECL(right_side, (int, int, int, char *));
2300 STATIC_DCL void FDECL(left_side, (int, int, int, char *));
2302 /* Initialize algorithm C (nothing). */
2303 STATIC_OVL void
2304 view_init()
2309 * Mark positions as visible on one quadrant of the right side. The
2310 * quadrant is determined by the value of the global variable step.
2312 STATIC_OVL void
2313 right_side(row, left, right_mark, limits)
2314 int row; /* current row */
2315 int left; /* first (left side) visible spot on prev row */
2316 int right_mark; /* last (right side) visible spot on prev row */
2317 char *limits; /* points at range limit for current row, or NULL */
2319 int right; /* right limit of "could see" */
2320 int right_edge; /* right edge of an opening */
2321 int nrow; /* new row (calculate once) */
2322 int deeper; /* if TRUE, call self as needed */
2323 int result; /* set by q?_path() */
2324 register int i; /* loop counter */
2325 register char *rowp = NULL; /* row optimization */
2326 char *row_min = NULL; /* left most [used by macro set_min()] */
2327 char *row_max = NULL; /* right most [used by macro set_max()] */
2328 int lim_max; /* right most limit of circle */
2330 nrow = row + step;
2332 * Can go deeper if the row is in bounds and the next row is within
2333 * the circle's limit. We tell the latter by checking to see if the next
2334 * limit value is the start of a new circle radius (meaning we depend
2335 * on the structure of circle_data[]).
2337 deeper = good_row(nrow) && (!limits || (*limits >= *(limits + 1)));
2338 if (!vis_func) {
2339 rowp = cs_rows[row]; /* optimization */
2340 row_min = &cs_left[row];
2341 row_max = &cs_right[row];
2343 if (limits) {
2344 lim_max = start_col + *limits;
2345 if (lim_max > COLNO - 1)
2346 lim_max = COLNO - 1;
2347 if (right_mark > lim_max)
2348 right_mark = lim_max;
2349 limits++; /* prepare for next row */
2350 } else
2351 lim_max = COLNO - 1;
2353 while (left <= right_mark) {
2354 right_edge = right_ptrs[row][left];
2355 if (right_edge > lim_max)
2356 right_edge = lim_max;
2358 if (!is_clear(row, left)) {
2360 * Jump to the far side of a stone wall. We can set all
2361 * the points in between as seen.
2363 * If the right edge goes beyond the right mark, check to see
2364 * how much we can see.
2366 if (right_edge > right_mark) {
2368 * If the mark on the previous row was a clear position,
2369 * the odds are that we can actually see part of the wall
2370 * beyond the mark on this row. If so, then see one beyond
2371 * the mark. Otherwise don't. This is a kludge so corners
2372 * with an adjacent doorway show up in nethack.
2374 right_edge = is_clear(row - step, right_mark) ? right_mark + 1
2375 : right_mark;
2377 if (vis_func) {
2378 for (i = left; i <= right_edge; i++)
2379 (*vis_func)(i, row, varg);
2380 } else {
2381 for (i = left; i <= right_edge; i++)
2382 set_cs(rowp, i);
2383 set_min(left);
2384 set_max(right_edge);
2386 left = right_edge + 1; /* no limit check necessary */
2387 continue;
2390 /* No checking needed if our left side is the start column. */
2391 if (left != start_col) {
2393 * Find the left side. Move right until we can see it or we run
2394 * into a wall.
2396 for (; left <= right_edge; left++) {
2397 if (step < 0) {
2398 q1_path(start_row, start_col, row, left, rside1);
2399 } else {
2400 q4_path(start_row, start_col, row, left, rside1);
2402 rside1: /* used if q?_path() is a macro */
2403 if (result)
2404 break;
2408 * Check for boundary conditions. We *need* check (2) to break
2409 * an infinite loop where:
2411 * left == right_edge == right_mark == lim_max.
2414 if (left > lim_max)
2415 return; /* check (1) */
2416 if (left == lim_max) { /* check (2) */
2417 if (vis_func) {
2418 (*vis_func)(lim_max, row, varg);
2419 } else {
2420 set_cs(rowp, lim_max);
2421 set_max(lim_max);
2423 return;
2426 * Check if we can see any spots in the opening. We might
2427 * (left == right_edge) or might not (left == right_edge+1) have
2428 * been able to see the far wall. Make sure we *can* see the
2429 * wall (remember, we can see the spot above/below this one)
2430 * by backing up.
2432 if (left >= right_edge) {
2433 left = right_edge; /* for the case left == right_edge+1 */
2434 continue;
2439 * Find the right side. If the marker from the previous row is
2440 * closer than the edge on this row, then we have to check
2441 * how far we can see around the corner (under the overhang). Stop
2442 * at the first non-visible spot or we actually hit the far wall.
2444 * Otherwise, we know we can see the right edge of the current row.
2446 * This must be a strict less than so that we can always see a
2447 * horizontal wall, even if it is adjacent to us.
2449 if (right_mark < right_edge) {
2450 for (right = right_mark; right <= right_edge; right++) {
2451 if (step < 0) {
2452 q1_path(start_row, start_col, row, right, rside2);
2453 } else {
2454 q4_path(start_row, start_col, row, right, rside2);
2456 rside2: /* used if q?_path() is a macro */
2457 if (!result)
2458 break;
2460 --right; /* get rid of the last increment */
2461 } else
2462 right = right_edge;
2465 * We have the range that we want. Set the bits. Note that
2466 * there is no else --- we no longer handle splinters.
2468 if (left <= right) {
2470 * An ugly special case. If you are adjacent to a vertical wall
2471 * and it has a break in it, then the right mark is set to be
2472 * start_col. We *want* to be able to see adjacent vertical
2473 * walls, so we have to set it back.
2475 if (left == right && left == start_col && start_col < (COLNO - 1)
2476 && !is_clear(row, start_col + 1))
2477 right = start_col + 1;
2479 if (right > lim_max)
2480 right = lim_max;
2481 /* set the bits */
2482 if (vis_func) {
2483 for (i = left; i <= right; i++)
2484 (*vis_func)(i, row, varg);
2485 } else {
2486 for (i = left; i <= right; i++)
2487 set_cs(rowp, i);
2488 set_min(left);
2489 set_max(right);
2492 /* recursive call for next finger of light */
2493 if (deeper)
2494 right_side(nrow, left, right, limits);
2495 left = right + 1; /* no limit check necessary */
2501 * This routine is the mirror image of right_side(). See right_side() for
2502 * extensive comments.
2504 STATIC_OVL void
2505 left_side(row, left_mark, right, limits)
2506 int row, left_mark, right;
2507 char *limits;
2509 int left, left_edge, nrow, deeper, result;
2510 register int i;
2511 register char *rowp = NULL;
2512 char *row_min = NULL;
2513 char *row_max = NULL;
2514 int lim_min;
2516 #ifdef GCC_WARN
2517 rowp = row_min = row_max = 0;
2518 #endif
2519 nrow = row + step;
2520 deeper = good_row(nrow) && (!limits || (*limits >= *(limits + 1)));
2521 if (!vis_func) {
2522 rowp = cs_rows[row];
2523 row_min = &cs_left[row];
2524 row_max = &cs_right[row];
2526 if (limits) {
2527 lim_min = start_col - *limits;
2528 if (lim_min < 0)
2529 lim_min = 0;
2530 if (left_mark < lim_min)
2531 left_mark = lim_min;
2532 limits++; /* prepare for next row */
2533 } else
2534 lim_min = 0;
2536 while (right >= left_mark) {
2537 left_edge = left_ptrs[row][right];
2538 if (left_edge < lim_min)
2539 left_edge = lim_min;
2541 if (!is_clear(row, right)) {
2542 /* Jump to the far side of a stone wall. */
2543 if (left_edge < left_mark) {
2544 /* Maybe see more (kludge). */
2545 left_edge = is_clear(row - step, left_mark) ? left_mark - 1
2546 : left_mark;
2548 if (vis_func) {
2549 for (i = left_edge; i <= right; i++)
2550 (*vis_func)(i, row, varg);
2551 } else {
2552 for (i = left_edge; i <= right; i++)
2553 set_cs(rowp, i);
2554 set_min(left_edge);
2555 set_max(right);
2557 right = left_edge - 1; /* no limit check necessary */
2558 continue;
2561 if (right != start_col) {
2562 /* Find the right side. */
2563 for (; right >= left_edge; right--) {
2564 if (step < 0) {
2565 q2_path(start_row, start_col, row, right, lside1);
2566 } else {
2567 q3_path(start_row, start_col, row, right, lside1);
2569 lside1: /* used if q?_path() is a macro */
2570 if (result)
2571 break;
2574 /* Check for boundary conditions. */
2575 if (right < lim_min)
2576 return;
2577 if (right == lim_min) {
2578 if (vis_func) {
2579 (*vis_func)(lim_min, row, varg);
2580 } else {
2581 set_cs(rowp, lim_min);
2582 set_min(lim_min);
2584 return;
2586 /* Check if we can see any spots in the opening. */
2587 if (right <= left_edge) {
2588 right = left_edge;
2589 continue;
2593 /* Find the left side. */
2594 if (left_mark > left_edge) {
2595 for (left = left_mark; left >= left_edge; --left) {
2596 if (step < 0) {
2597 q2_path(start_row, start_col, row, left, lside2);
2598 } else {
2599 q3_path(start_row, start_col, row, left, lside2);
2601 lside2: /* used if q?_path() is a macro */
2602 if (!result)
2603 break;
2605 left++; /* get rid of the last decrement */
2606 } else
2607 left = left_edge;
2609 if (left <= right) {
2610 /* An ugly special case. */
2611 if (left == right && right == start_col && start_col > 0
2612 && !is_clear(row, start_col - 1))
2613 left = start_col - 1;
2615 if (left < lim_min)
2616 left = lim_min;
2617 if (vis_func) {
2618 for (i = left; i <= right; i++)
2619 (*vis_func)(i, row, varg);
2620 } else {
2621 for (i = left; i <= right; i++)
2622 set_cs(rowp, i);
2623 set_min(left);
2624 set_max(right);
2627 /* Recurse */
2628 if (deeper)
2629 left_side(nrow, left, right, limits);
2630 right = left - 1; /* no limit check necessary */
2636 * Calculate all possible visible locations from the given location
2637 * (srow,scol). NOTE this is (y,x)! Mark the visible locations in the
2638 * array provided.
2640 STATIC_OVL void
2641 view_from(srow, scol, loc_cs_rows, left_most, right_most, range, func, arg)
2642 int srow, scol; /* starting row and column */
2643 char **loc_cs_rows; /* pointers to the rows of the could_see array */
2644 char *left_most; /* min mark on each row */
2645 char *right_most; /* max mark on each row */
2646 int range; /* 0 if unlimited */
2647 void FDECL((*func), (int, int, genericptr_t));
2648 genericptr_t arg;
2650 register int i; /* loop counter */
2651 char *rowp; /* optimization for setting could_see */
2652 int nrow; /* the next row */
2653 int left; /* the left-most visible column */
2654 int right; /* the right-most visible column */
2655 char *limits; /* range limit for next row */
2657 /* Set globals for q?_path(), left_side(), and right_side() to use. */
2658 start_col = scol;
2659 start_row = srow;
2660 cs_rows = loc_cs_rows; /* 'could see' rows */
2661 cs_left = left_most;
2662 cs_right = right_most;
2663 vis_func = func;
2664 varg = arg;
2667 * Determine extent of sight on the starting row.
2669 if (is_clear(srow, scol)) {
2670 left = left_ptrs[srow][scol];
2671 right = right_ptrs[srow][scol];
2672 } else {
2674 * When in stone, you can only see your adjacent squares, unless
2675 * you are on an array boundary or a stone/clear boundary.
2677 left = (!scol) ? 0
2678 : (is_clear(srow, scol - 1) ? left_ptrs[srow][scol - 1]
2679 : scol - 1);
2680 right = (scol == COLNO - 1)
2681 ? COLNO - 1
2682 : (is_clear(srow, scol + 1) ? right_ptrs[srow][scol + 1]
2683 : scol + 1);
2686 if (range) {
2687 if (range > MAX_RADIUS || range < 1)
2688 panic("view_from called with range %d", range);
2689 limits = circle_ptr(range) + 1; /* start at next row */
2690 if (left < scol - range)
2691 left = scol - range;
2692 if (right > scol + range)
2693 right = scol + range;
2694 } else
2695 limits = (char *) 0;
2697 if (func) {
2698 for (i = left; i <= right; i++)
2699 (*func)(i, srow, arg);
2700 } else {
2701 /* Row pointer optimization. */
2702 rowp = cs_rows[srow];
2704 /* We know that we can see our row. */
2705 for (i = left; i <= right; i++)
2706 set_cs(rowp, i);
2707 cs_left[srow] = left;
2708 cs_right[srow] = right;
2712 * Check what could be seen in quadrants. We need to check for valid
2713 * rows here, since we don't do it in the routines right_side() and
2714 * left_side() [ugliness to remove extra routine calls].
2716 if ((nrow = srow + 1) < ROWNO) { /* move down */
2717 step = 1;
2718 if (scol < COLNO - 1)
2719 right_side(nrow, scol, right, limits);
2720 if (scol)
2721 left_side(nrow, left, scol, limits);
2724 if ((nrow = srow - 1) >= 0) { /* move up */
2725 step = -1;
2726 if (scol < COLNO - 1)
2727 right_side(nrow, scol, right, limits);
2728 if (scol)
2729 left_side(nrow, left, scol, limits);
2733 #endif /*===== End of algorithm C =====*/
2736 * AREA OF EFFECT "ENGINE"
2738 * Calculate all possible visible locations as viewed from the given location
2739 * (srow,scol) within the range specified. Perform "func" with (x, y) args and
2740 * additional argument "arg" for each square.
2742 * If not centered on the hero, just forward arguments to view_from(); it
2743 * will call "func" when necessary. If the hero is the center, use the
2744 * vision matrix and reduce extra work.
2746 void
2747 do_clear_area(scol, srow, range, func, arg)
2748 int scol, srow, range;
2749 void FDECL((*func), (int, int, genericptr_t));
2750 genericptr_t arg;
2752 /* If not centered on hero, do the hard work of figuring the area */
2753 if (scol != u.ux || srow != u.uy) {
2754 view_from(srow, scol, (char **) 0, (char *) 0, (char *) 0, range,
2755 func, arg);
2756 } else {
2757 register int x;
2758 int y, min_x, max_x, max_y, offset;
2759 char *limits;
2760 boolean override_vision;
2762 /* vision doesn't pass through water or clouds, detection should
2763 [this probably ought to be an arg supplied by our caller...] */
2764 override_vision =
2765 (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) && detecting(func);
2767 if (range > MAX_RADIUS || range < 1)
2768 panic("do_clear_area: illegal range %d", range);
2769 if (vision_full_recalc)
2770 vision_recalc(0); /* recalc vision if dirty */
2771 limits = circle_ptr(range);
2772 if ((max_y = (srow + range)) >= ROWNO)
2773 max_y = ROWNO - 1;
2774 if ((y = (srow - range)) < 0)
2775 y = 0;
2776 for (; y <= max_y; y++) {
2777 offset = limits[v_abs(y - srow)];
2778 if ((min_x = (scol - offset)) < 0)
2779 min_x = 0;
2780 if ((max_x = (scol + offset)) >= COLNO)
2781 max_x = COLNO - 1;
2782 for (x = min_x; x <= max_x; x++)
2783 if (couldsee(x, y) || override_vision)
2784 (*func)(x, y, arg);
2789 /* bitmask indicating ways mon is seen; extracted from lookat(pager.c) */
2790 unsigned
2791 howmonseen(mon)
2792 struct monst *mon;
2794 boolean useemon = (boolean) canseemon(mon);
2795 int xraydist = (u.xray_range < 0) ? -1 : (u.xray_range * u.xray_range);
2796 unsigned how_seen = 0; /* result */
2798 /* normal vision;
2799 cansee is true for both normal and astral vision,
2800 but couldsee it not true for astral vision */
2801 if ((mon->wormno ? worm_known(mon) : (cansee(mon->mx, mon->my)
2802 && couldsee(mon->mx, mon->my)))
2803 && mon_visible(mon) && !mon->minvis)
2804 how_seen |= MONSEEN_NORMAL;
2805 /* see invisible */
2806 if (useemon && mon->minvis)
2807 how_seen |= MONSEEN_SEEINVIS;
2808 /* infravision */
2809 if ((!mon->minvis || See_invisible) && see_with_infrared(mon))
2810 how_seen |= MONSEEN_INFRAVIS;
2811 /* telepathy */
2812 if (tp_sensemon(mon))
2813 how_seen |= MONSEEN_TELEPAT;
2814 /* xray */
2815 if (useemon && xraydist > 0 && distu(mon->mx, mon->my) <= xraydist)
2816 how_seen |= MONSEEN_XRAYVIS;
2817 /* extended detection */
2818 if (Detect_monsters)
2819 how_seen |= MONSEEN_DETECT;
2820 /* class-/type-specific warning */
2821 if (MATCH_WARN_OF_MON(mon))
2822 how_seen |= MONSEEN_WARNMON;
2824 return how_seen;
2827 /*vision.c*/