Blindfold removal fix
[slashemextended.git] / src / vision.c
blob730993ae1eb8098bd28f2eebf263f75b9c4b3dc9
1 /* SCCS Id: @(#)vision.c 3.4 1999/02/18 */
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 ==================================================================*/
9 /*
10 * These numbers are limit offsets for one quadrant of a circle of a given
11 * radius (the first number of each line) from the source. The number in
12 * the comment is the element number (so pointers can be set up). Each
13 * "circle" has as many elements as its radius+1. The radius is the number
14 * of points away from the source that the limit exists. The radius of the
15 * offset on the same row as the source *is* included so we don't have to
16 * make an extra check. For example, a circle of radius 4 has offsets:
18 * XXX +2
19 * ...X +3
20 * ....X +4
21 * ....X +4
22 * @...X +4
25 char circle_data[] = {
26 /* 0*/ 1, 1,
27 /* 2*/ 2, 2, 1,
28 /* 5*/ 3, 3, 2, 1,
29 /* 9*/ 4, 4, 4, 3, 2,
30 /* 14*/ 5, 5, 5, 4, 3, 2,
31 /* 20*/ 6, 6, 6, 5, 5, 4, 2,
32 /* 27*/ 7, 7, 7, 6, 6, 5, 4, 2,
33 /* 35*/ 8, 8, 8, 7, 7, 6, 6, 4, 2,
34 /* 44*/ 9, 9, 9, 9, 8, 8, 7, 6, 5, 3,
35 /* 54*/ 10,10,10,10, 9, 9, 8, 7, 6, 5, 3,
36 /* 65*/ 11,11,11,11,10,10, 9, 9, 8, 7, 5, 3,
37 /* 77*/ 12,12,12,12,11,11,10,10, 9, 8, 7, 5, 3,
38 /* 90*/ 13,13,13,13,12,12,12,11,10,10, 9, 7, 6, 3,
39 /*104*/ 14,14,14,14,13,13,13,12,12,11,10, 9, 8, 6, 3,
40 /*119*/ 15,15,15,15,14,14,14,13,13,12,11,10, 9, 8, 6, 3,
41 /*135*/ 16 /* should be MAX_RADIUS+1; used to terminate range loops -dlc */
45 * These are the starting indexes into the circle_data[] array for a
46 * circle of a given radius.
48 char circle_start[] = {
49 /* */ 0, /* circles of radius zero are not used */
50 /* 1*/ 0,
51 /* 2*/ 2,
52 /* 3*/ 5,
53 /* 4*/ 9,
54 /* 5*/ 14,
55 /* 6*/ 20,
56 /* 7*/ 27,
57 /* 8*/ 35,
58 /* 9*/ 44,
59 /*10*/ 54,
60 /*11*/ 65,
61 /*12*/ 77,
62 /*13*/ 90,
63 /*14*/ 104,
64 /*15*/ 119,
68 /*===========================================================================*/
69 /* Vision (arbitrary line of sight) =========================================*/
71 /*------ global variables ------*/
73 #if 0 /* (moved to decl.c) */
74 /* True if we need to run a full vision recalculation. */
75 boolean vision_full_recalc = 0;
77 /* Pointers to the current vision array. */
78 char **viz_array;
79 #endif
80 char *viz_rmin, *viz_rmax; /* current vision cs bounds */
83 /*------ local variables ------*/
86 static char could_see[2][ROWNO][COLNO]; /* vision work space */
87 static char *cs_rows0[ROWNO], *cs_rows1[ROWNO];
88 static char cs_rmin0[ROWNO], cs_rmax0[ROWNO];
89 static char cs_rmin1[ROWNO], cs_rmax1[ROWNO];
91 static char viz_clear[ROWNO][COLNO]; /* vision clear/blocked map */
92 static char *viz_clear_rows[ROWNO];
94 static char left_ptrs[ROWNO][COLNO]; /* LOS algorithm helpers */
95 static char right_ptrs[ROWNO][COLNO];
97 /* Forward declarations. */
98 STATIC_DCL void fill_point(int,int);
99 STATIC_DCL void dig_point(int,int);
100 STATIC_DCL void view_init(void);
101 STATIC_DCL void view_from(int,int,char **,char *,char *,int, void (*)(int,int,void *),void *);
102 STATIC_DCL void get_unused_cs(char ***,char **,char **);
103 #ifdef REINCARNATION
104 STATIC_DCL void rogue_vision(char **,char *,char *);
105 #endif
107 /* Macro definitions that I can't find anywhere. */
108 #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0 ))
109 #define v_abs(z) ((z) < 0 ? -(z) : (z)) /* don't use abs -- it may exist */
112 * vision_init()
114 * The one-time vision initialization routine.
116 * This must be called before mklev() is called in newgame() [allmain.c],
117 * or before a game restore. Else we die a horrible death.
119 void
120 vision_init()
122 int i;
124 /* Set up the pointers. */
125 for (i = 0; i < ROWNO; i++) {
126 cs_rows0[i] = could_see[0][i];
127 cs_rows1[i] = could_see[1][i];
128 viz_clear_rows[i] = viz_clear[i];
131 /* Start out with cs0 as our current array */
132 viz_array = cs_rows0;
133 viz_rmin = cs_rmin0;
134 viz_rmax = cs_rmax0;
136 vision_full_recalc = 0;
137 (void) memset((void *) could_see, 0, sizeof(could_see));
139 /* Initialize the vision algorithm (currently C or D). */
140 view_init();
142 #ifdef VISION_TABLES
143 /* Note: this initializer doesn't do anything except guarantee that
144 we're linked properly.
146 vis_tab_init();
147 #endif
151 * does_block()
153 * Returns true if the level feature, object, or monster at (x,y) blocks
154 * sight.
157 does_block(x,y,lev)
158 int x, y;
159 register struct rm *lev;
161 struct obj *obj;
162 struct monst *mon;
164 /* Features that block . . */
165 /* KMH -- added trees */
166 if ( ( (IS_ROCK(lev->typ) && !(IS_FARMLAND(lev->typ))) || lev->typ == TREE || lev->typ == WATERTUNNEL || ((IS_DOOR(lev->typ))
167 && (lev->doormask & (D_CLOSED|D_LOCKED|D_TRAPPED) ))) )
168 return 1;
170 if (lev->typ == CLOUD || lev->typ == BUBBLES || lev->typ == RAINCLOUD || lev->typ == WATER ||
171 (lev->typ == MOAT && Underwater && !Swimming && !(uwep && uwep->oartifact == ART_SEE_THE_REST_OF_THE_WORLD) ))
172 return 1;
174 /* Boulders block light. */
175 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
176 if (obj->otyp == BOULDER) return 1;
178 /* Mimics mimicing a door or boulder block light. */
179 if ((mon = m_at(x,y)) && (!mon->minvis || See_invisible) && !mon->minvisreal &&
180 ((mon->m_ap_type == M_AP_FURNITURE &&
181 (mon->mappearance == S_hcdoor || mon->mappearance == S_vcdoor)) ||
182 (mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER)))
183 return 1;
185 if (TezActive && m_at(x,y)) return 1;
187 return 0;
191 * vision_reset()
193 * This must be called *after* the levl[][] structure is set with the new
194 * level and the level monsters and objects are in place.
196 void
197 vision_reset()
199 int y;
200 register int x, i, dig_left, block;
201 register struct rm *lev;
203 /* Start out with cs0 as our current array */
204 viz_array = cs_rows0;
205 viz_rmin = cs_rmin0;
206 viz_rmax = cs_rmax0;
208 (void) memset((void *) could_see, 0, sizeof(could_see));
210 /* Reset the pointers and clear so that we have a "full" dungeon. */
211 (void) memset((void *) viz_clear, 0, sizeof(viz_clear));
213 /* Dig the level */
214 for (y = 0; y < ROWNO; y++) {
215 dig_left = 0;
216 block = TRUE; /* location (0,y) is always stone; it's !isok() */
217 lev = &levl[1][y];
218 for (x = 1; x < COLNO; x++, lev += ROWNO)
219 if (block != ((IS_ROCK(lev->typ) && !(IS_FARMLAND(lev->typ))) || does_block(x,y,lev))) {
220 if(block) {
221 for(i=dig_left; i<x; i++) {
222 left_ptrs [y][i] = dig_left;
223 right_ptrs[y][i] = x-1;
225 } else {
226 i = dig_left;
227 if(dig_left) dig_left--; /* point at first blocked point */
228 for(; i<x; i++) {
229 left_ptrs [y][i] = dig_left;
230 right_ptrs[y][i] = x;
231 viz_clear[y][i] = 1;
232 if (TezActive && m_at(y,i)) viz_clear[y][i] = 0;
235 dig_left = x;
236 block = !block;
238 /* handle right boundary; almost identical for blocked/unblocked */
239 i = dig_left;
240 if(!block && dig_left) dig_left--; /* point at first blocked point */
241 for(; i<COLNO; i++) {
242 left_ptrs [y][i] = dig_left;
243 right_ptrs[y][i] = (COLNO-1);
244 viz_clear[y][i] = !block;
245 if (TezActive && m_at(y,i)) viz_clear[y][i] = 0;
249 iflags.vision_inited = 1; /* vision is ready */
250 vision_full_recalc = 1; /* we want to run vision_recalc() */
255 * get_unused_cs()
257 * Called from vision_recalc() and at least one light routine. Get pointers
258 * to the unused vision work area.
260 STATIC_OVL void
261 get_unused_cs(rows, rmin, rmax)
262 char ***rows;
263 char **rmin, **rmax;
265 register int row;
266 register char *nrmin, *nrmax;
268 if (viz_array == cs_rows0) {
269 *rows = cs_rows1;
270 *rmin = cs_rmin1;
271 *rmax = cs_rmax1;
272 } else {
273 *rows = cs_rows0;
274 *rmin = cs_rmin0;
275 *rmax = cs_rmax0;
278 /* return an initialized, unused work area */
279 nrmin = *rmin;
280 nrmax = *rmax;
282 (void) memset((void *)**rows, 0, ROWNO*COLNO); /* we see nothing */
283 for (row = 0; row < ROWNO; row++) { /* set row min & max */
284 *nrmin++ = COLNO-1;
285 *nrmax++ = 0;
290 #ifdef REINCARNATION
292 * rogue_vision()
294 * Set the "could see" and in sight bits so vision acts just like the old
295 * rogue game:
297 * + If in a room, the hero can see to the room boundaries.
298 * + The hero can always see adjacent squares.
300 * We set the in_sight bit here as well to escape a bug that shows up
301 * due to the one-sided lit wall hack.
303 STATIC_OVL void
304 rogue_vision(next, rmin, rmax)
305 char **next; /* could_see array pointers */
306 char *rmin, *rmax;
308 int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET; /* no SHARED... */
309 int start, stop, in_door, xhi, xlo, yhi, ylo;
310 register int zx, zy;
312 /* If in a lit room, we are able to see to its boundaries. */
313 /* If dark, set COULD_SEE so various spells work -dlc */
314 if (rnum >= 0) {
315 for (zy = rooms[rnum].ly-1; zy <= rooms[rnum].hy+1; zy++) {
316 rmin[zy] = start = rooms[rnum].lx-1;
317 rmax[zy] = stop = rooms[rnum].hx+1;
319 for (zx = start; zx <= stop; zx++) {
320 if (rooms[rnum].rlit) {
321 next[zy][zx] = COULD_SEE | IN_SIGHT;
322 levl[zx][zy].seenv = SVALL; /* see the walls */
323 } else
324 next[zy][zx] = COULD_SEE;
329 in_door = levl[u.ux][u.uy].typ == DOOR;
331 /* Can always see adjacent. */
332 ylo = max(u.uy - 1, 0);
333 yhi = min(u.uy + 1, ROWNO - 1);
334 xlo = max(u.ux - 1, 1);
335 xhi = min(u.ux + 1, COLNO - 1);
336 for (zy = ylo; zy <= yhi; zy++) {
337 if (xlo < rmin[zy]) rmin[zy] = xlo;
338 if (xhi > rmax[zy]) rmax[zy] = xhi;
340 for (zx = xlo; zx <= xhi; zx++) {
341 next[zy][zx] = COULD_SEE | IN_SIGHT;
343 * Yuck, update adjacent non-diagonal positions when in a doorway.
344 * We need to do this to catch the case when we first step into
345 * a room. The room's walls were not seen from the outside, but
346 * now are seen (the seen bits are set just above). However, the
347 * positions are not updated because they were already in sight.
348 * So, we have to do it here.
350 if (in_door && (zx == u.ux || zy == u.uy)) newsym(zx,zy);
354 #endif /* REINCARNATION */
356 /*#define EXTEND_SPINE*/ /* possibly better looking wall-angle */
358 #ifdef EXTEND_SPINE
360 STATIC_DCL int new_angle(struct rm *, unsigned char *, int, int);
362 * new_angle()
364 * Return the new angle seen by the hero for this location. The angle
365 * bit is given in the value pointed at by sv.
367 * For T walls and crosswall, just setting the angle bit, even though
368 * it is technically correct, doesn't look good. If we can see the
369 * next position beyond the current one and it is a wall that we can
370 * see, then we want to extend a spine of the T to connect with the wall
371 * that is beyond. Example:
373 * Correct, but ugly Extend T spine
375 * | ... | ...
376 * | ... <-- wall beyond & floor --> | ...
377 * | ... | ...
378 * Unseen --> ... | ...
379 * spine +-... <-- trwall & doorway --> +-...
380 * | ... | ...
383 * @ <-- hero --> @
386 * We fake the above check by only checking if the horizontal &
387 * vertical positions adjacent to the crosswall and T wall are
388 * unblocked. Then, _in general_ we can see beyond. Generally,
389 * this is good enough.
391 * + When this function is called we don't have all of the seen
392 * information (we're doing a top down scan in vision_recalc).
393 * We would need to scan once to set all IN_SIGHT and COULD_SEE
394 * bits, then again to correctly set the seenv bits.
395 * + I'm trying to make this as cheap as possible. The display &
396 * vision eat up too much CPU time.
399 * Note: Even as I write this, I'm still not convinced. There are too
400 * many exceptions. I may have to bite the bullet and do more
401 * checks. - Dean 2/11/93
403 STATIC_OVL int
404 new_angle(lev, sv, row, col)
405 struct rm *lev;
406 unsigned char *sv;
407 int row, col;
409 register int res = *sv;
412 * Do extra checks for crosswalls and T walls if we see them from
413 * an angle.
415 if (lev->typ >= CROSSWALL && lev->typ <= TRWALL) {
416 switch (res) {
417 case SV0:
418 if (col > 0 && viz_clear[row][col-1]) res |= SV7;
419 if (row > 0 && viz_clear[row-1][col]) res |= SV1;
420 break;
421 case SV2:
422 if (row > 0 && viz_clear[row-1][col]) res |= SV1;
423 if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3;
424 break;
425 case SV4:
426 if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3;
427 if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5;
428 break;
429 case SV6:
430 if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5;
431 if (col > 0 && viz_clear[row][col-1]) res |= SV7;
432 break;
435 return res;
437 #else
439 * new_angle()
441 * Return the new angle seen by the hero for this location. The angle
442 * bit is given in the value pointed at by sv.
444 * The other parameters are not used.
446 #define new_angle(lev, sv, row, col) (*sv)
448 #endif
452 * vision_recalc()
454 * Do all of the heavy vision work. Recalculate all locations that could
455 * possibly be seen by the hero --- if the location were lit, etc. Note
456 * which locations are actually seen because of lighting. Then add to
457 * this all locations that be seen by hero due to night vision and x-ray
458 * vision. Finally, compare with what the hero was able to see previously.
459 * Update the difference.
461 * This function is usually called only when the variable 'vision_full_recalc'
462 * is set. The following is a list of places where this function is called,
463 * with three valid values for the control flag parameter:
465 * Control flag = 0. A complete vision recalculation. Generate the vision
466 * tables from scratch. This is necessary to correctly set what the hero
467 * can see. (1) and (2) call this routine for synchronization purposes, (3)
468 * calls this routine so it can operate correctly.
470 * + After the monster move, before input from the player. [moveloop()]
471 * + At end of moveloop. [moveloop() ??? not sure why this is here]
472 * + Right before something is printed. [pline()]
473 * + Right before we do a vision based operation. [do_clear_area()]
474 * + screen redraw, so we can renew all positions in sight. [docrt()]
475 *WAC + when firing wand of fire [buzz()] #define LIGHT_SRC_SPELL
476 *WAC + fire explosions [explode()] #define LIGHT_SRC_SPELL
478 * Control flag = 1. An adjacent vision recalculation. The hero has moved
479 * one square. Knowing this, it might be possible to optimize the vision
480 * recalculation using the current knowledge. This is presently unimplemented
481 * and is treated as a control = 0 call.
483 * + Right after the hero moves. [domove()]
485 * Control flag = 2. Turn off the vision system. Nothing new will be
486 * displayed, since nothing is seen. This is usually done when you need
487 * a newsym() run on all locations in sight, or on some locations but you
488 * don't know which ones.
490 * + Before a screen redraw, so all positions are renewed. [docrt()]
491 * + Right before the hero arrives on a new level. [goto_level()]
492 * + Right after a scroll of light is read. [litroom()]
493 * + After an option has changed that affects vision [parseoptions()]
494 * + Right after the hero is swallowed. [gulpmu()]
495 * + Just before bubbles are moved. [movebubbles()]
497 * Control flag = 5. For interface screw trap, which requires the player
498 * to press Ctrl-R to see what has happened in the game. --Amy
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; /* 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 */
521 int efflightradius;
523 vision_full_recalc = 0; /* reset flag */
524 if (in_mklev || !iflags.vision_inited) return;
526 if ((InterfaceScrewed || u.uprops[INTERFACE_SCREW].extrinsic || have_interfacescrewstone()) && control != 5) return;
527 if (control == 5) control = 0;
529 #ifdef GCC_WARN
530 row = 0;
531 #endif
534 * Either the light sources have been taken care of, or we must
535 * recalculate them here.
538 /* Get the unused could see, row min, and row max arrays. */
539 get_unused_cs(&next_array, &next_rmin, &next_rmax);
541 /* You see nothing, nothing can see you --- if swallowed or refreshing. */
542 if (u.uswallow || control == 2) {
543 /* do nothing -- get_unused_cs() nulls out the new work area */
545 } else if (Blind) {
547 * Calculate the could_see array even when blind so that monsters
548 * can see you, even if you can't see them. Note that the current
549 * setup allows:
551 * + Monsters to see with the "new" vision, even on the rogue
552 * level.
554 * + Monsters can see you even when you're in a pit.
556 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax,
557 0, (void (*)(int,int,void *))0, (void *)0);
560 * Our own version of the update loop below. We know we can't see
561 * anything, so we only need update positions we used to be able
562 * to see.
564 temp_array = viz_array; /* set viz_array so newsym() will work */
565 viz_array = next_array;
567 for (row = 0; row < ROWNO; row++) {
568 old_row = temp_array[row];
570 /* Find the min and max positions on the row. */
571 start = min(viz_rmin[row], next_rmin[row]);
572 stop = max(viz_rmax[row], next_rmax[row]);
574 for (col = start; col <= stop; col++)
575 if (old_row[col] & IN_SIGHT) newsym(col,row);
578 /* skip the normal update loop */
579 goto skip;
581 #ifdef REINCARNATION
582 else if (Is_rogue_level(&u.uz)) {
583 rogue_vision(next_array,next_rmin,next_rmax);
585 #endif
586 else {
587 int has_night_vision = 1; /* hero has night vision */
589 if (Underwater && !Is_waterlevel(&u.uz) && (!Swimming || (uwep && uwep->oartifact == ART_SPACEL_SWIM) || (uwep && uwep->oartifact == ART_SEE_THE_REST_OF_THE_WORLD) ) ) {
591 * The hero is under water. Only see surrounding locations if
592 * they are also underwater. This overrides night vision but
593 * does not override x-ray vision.
595 has_night_vision = 0;
597 if ((Swimming && uwep && uwep->oartifact == ART_SPACEL_SWIM) || (uwep && uwep->oartifact == ART_SEE_THE_REST_OF_THE_WORLD)) {
598 /* originally a bug, enabled for artifact --Amy */
600 for (row = 0; row <= ROWNO; row++)
601 for (col = 0; col <= COLNO; col++) {
602 if (!isok(col,row)) continue;
604 next_rmin[row] = min(next_rmin[row], col);
605 next_rmax[row] = max(next_rmax[row], col);
606 next_array[row][col] = IN_SIGHT | COULD_SEE;
609 } else {
611 for (row = u.uy-1; row <= u.uy+1; row++)
612 for (col = u.ux-1; col <= u.ux+1; col++) {
613 if (!isok(col,row) || (!is_waterypool(col,row)) ) continue;
615 next_rmin[row] = min(next_rmin[row], col);
616 next_rmax[row] = max(next_rmax[row], col);
617 next_array[row][col] = IN_SIGHT | COULD_SEE;
622 /* if in a pit, just update for immediate locations */
623 else if (u.utrap && u.utraptype == TT_PIT) {
624 for (row = u.uy-1; row <= u.uy+1; row++) {
625 if (row < 0) continue; if (row >= ROWNO) break;
627 next_rmin[row] = max( 0, u.ux - 1);
628 next_rmax[row] = min(COLNO-1, u.ux + 1);
629 next_row = next_array[row];
631 for(col=next_rmin[row]; col <= next_rmax[row]; col++)
632 next_row[col] = IN_SIGHT | COULD_SEE;
634 } else
635 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax,
636 0,(void(*)(int, int, void *))0, (void *)0);
639 * Set the IN_SIGHT bit for xray and night vision.
641 if (u.xray_range >= 0) {
642 if (u.xray_range) {
643 ranges = circle_ptr(u.xray_range);
645 for (row = u.uy-u.xray_range; row <= u.uy+u.xray_range; row++) {
646 if (row < 0) continue; if (row >= ROWNO) break;
647 dy = v_abs(u.uy-row); next_row = next_array[row];
649 start = max( 0, u.ux - ranges[dy]);
650 stop = min(COLNO-1, u.ux + ranges[dy]);
652 for (col = start; col <= stop; col++) {
653 char old_row_val = next_row[col];
654 next_row[col] |= IN_SIGHT;
655 oldseenv = levl[col][row].seenv;
656 levl[col][row].seenv = SVALL; /* see all! */
657 /* Update if previously not in sight or new angle. */
658 if (!(old_row_val & IN_SIGHT) || oldseenv != SVALL)
659 newsym(col,row);
662 next_rmin[row] = min(start, next_rmin[row]);
663 next_rmax[row] = max(stop, next_rmax[row]);
666 } else { /* range is 0 */
667 next_array[u.uy][u.ux] |= IN_SIGHT;
668 levl[u.ux][u.uy].seenv = SVALL;
669 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
670 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
674 efflightradius = (u.nv_range + Sight_bonus + StrongSight_bonus);
675 if (uarmh && uarmh->oartifact == ART_DARKSIGHT_HELM) efflightradius += 2;
676 if (uarm && uarm->oartifact == ART_SILKS_OF_THE_VICTOR) efflightradius += 1;
677 if (uwep && uwep->oartifact == ART_IT_BECOME_LIGHT) efflightradius += 2;
678 if (uwep && uwep->oartifact == ART_WONDERLIGHT) efflightradius += 2;
679 if (uwep && uwep->oartifact == ART_SEEVEEN) efflightradius += 2;
680 if (uwep && uwep->oartifact == ART_NURSING_THE_FLAME) efflightradius += 1;
681 if (uwep && uwep->oartifact == ART_FEUBURN) efflightradius += 1;
682 if (uwep && uwep->oartifact == ART_DARKLITE && uwep->lamplit) efflightradius += 2;
683 if (uwep && uwep->oartifact == ART_ULTRA_ANNOYANCE) efflightradius += 2;
684 if (uarm && uarm->oartifact == ART_FARTHER_INTO_THE_JUNGLE) efflightradius += 2;
685 if (uwep && uwep->oartifact == ART_IS_EVERYWHERE && uwep->lamplit) efflightradius += 4;
686 if (uwep && uwep->oartifact == ART_KRART_T_T_T_T) efflightradius += 2;
687 if (uarmf && uarmf->oartifact == ART_BRIGHT_AURORA) efflightradius += 2;
688 if (uarmf && uarmf->oartifact == ART_GIORDANA_S_RADIANCE) efflightradius += 2;
689 if (uwep && uwep->oartifact == ART_KIMYO_NI_HIKARU_SONZAI) efflightradius += 2;
690 if (uwep && uwep->oartifact == ART_GIGANTIC_SUN) efflightradius += 3;
691 if (uarm && uarm->oartifact == ART_HELP_WITH_THE_MINE && In_mines(&u.uz)) efflightradius += 2;
692 if (uarm && uarm->oartifact == ART_HELP_WITH_THE_MINE && In_deepmines(&u.uz)) efflightradius += 1;
693 if (uarmg && uarmg->oartifact == ART_FARTUBE) efflightradius += 9;
695 if (uarmg && uarmg->oartifact == ART_MAX_THE_SECRET_AGENT) efflightradius = MAX_RADIUS;
697 if (efflightradius > MAX_RADIUS) efflightradius = MAX_RADIUS; /* fail safe, why isn't that present in vanilla --Amy */
699 if (has_night_vision && !(u.uprops[WEAKSIGHT].extrinsic || (uarmg && itemhasappearance(uarmg, APP_TELESCOPE) && uarmg->cursed) || (Race_if(PM_ETHEREALOID) && !Upolyd) || (Race_if(PM_INCORPOREALOID) && !Upolyd) || (uwep && uwep->otyp == SNIPESLING) || (u.twoweap && uswapwep && uswapwep->otyp == SNIPESLING) || (uarmh && uarmh->oartifact == ART_WOLF_KING) || WeakSight || autismringcheck(ART_BLIND_PILOT) || have_weaksightstone() || (autismweaponcheck(ART_GREAT_MATRON) && !Role_if(PM_AMAZON)) || (uarmf && uarmf->oartifact == ART_DARK_BALL_OF_LIGHT) || (Race_if(PM_NEMESIS) && uarmh) ) && !autismweaponcheck(ART_WEAKITE_THRUST) && !(uarm && uarm->oartifact == ART_OVERRATED_FACE_PROTECTION) && !(uarmh && uarmh->oartifact == ART_FIRE_CHIEF_HELMET) && u.xray_range < efflightradius) {
700 if (!efflightradius) { /* range is 0 */
701 next_array[u.uy][u.ux] |= IN_SIGHT;
702 levl[u.ux][u.uy].seenv = SVALL;
703 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
704 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
705 } else if ((efflightradius > 0) && !(u.uprops[WEAKSIGHT].extrinsic || (uarmg && itemhasappearance(uarmg, APP_TELESCOPE) && uarmg->cursed) || (uwep && uwep->otyp == SNIPESLING) || (u.twoweap && uswapwep && uswapwep->otyp == SNIPESLING) || (uarmh && uarmh->oartifact == ART_WOLF_KING) || WeakSight || autismringcheck(ART_BLIND_PILOT) || (autismweaponcheck(ART_GREAT_MATRON) && !Role_if(PM_AMAZON)) || have_weaksightstone() || (uarmf && uarmf->oartifact == ART_DARK_BALL_OF_LIGHT) || (Race_if(PM_NEMESIS) && uarmh) || autismweaponcheck(ART_WEAKITE_THRUST) ) && !(uarm && uarm->oartifact == ART_OVERRATED_FACE_PROTECTION) && !(uarmh && uarmh->oartifact == ART_FIRE_CHIEF_HELMET) ) {
706 ranges = circle_ptr(efflightradius);
708 for (row = (u.uy - efflightradius); row <= (u.uy + efflightradius); row++) {
709 if (row < 0) continue; if (row >= ROWNO) break;
710 dy = v_abs(u.uy-row); next_row = next_array[row];
712 start = max( 0, u.ux - ranges[dy]);
713 stop = min(COLNO-1, u.ux + ranges[dy]);
715 for (col = start; col <= stop; col++)
716 if (next_row[col]) next_row[col] |= IN_SIGHT;
718 next_rmin[row] = min(start, next_rmin[row]);
719 next_rmax[row] = max(stop, next_rmax[row]);
725 /* Set the correct bits for all light sources. */
726 do_light_sources(next_array);
730 * Make the viz_array the new array so that cansee() will work correctly.
732 temp_array = viz_array;
733 viz_array = next_array;
736 * The main update loop. Here we do two things:
738 * + Set the IN_SIGHT bit for places that we could see and are lit.
739 * + Reset changed places.
741 * There is one thing that make deciding what the hero can see
742 * difficult:
744 * 1. Directional lighting. Items that block light create problems.
745 * The worst offenders are doors. Suppose a door to a lit room
746 * is closed. It is lit on one side, but not on the other. How
747 * do you know? You have to check the closest adjacent position.
748 * Even so, that is not entirely correct. But it seems close
749 * enough for now.
751 colbump[u.ux] = colbump[u.ux+1] = 1;
752 for (row = 0; row < ROWNO; row++) {
753 dy = u.uy - row; dy = sign(dy);
754 next_row = next_array[row]; old_row = temp_array[row];
756 /* Find the min and max positions on the row. */
757 start = min(viz_rmin[row], next_rmin[row]);
758 stop = max(viz_rmax[row], next_rmax[row]);
759 lev = &levl[start][row];
761 sv = &seenv_matrix[dy+1][start < u.ux ? 0 : (start > u.ux ? 2:1)];
763 for (col = start; col <= stop;
764 lev += ROWNO, sv += (int) colbump[++col]) {
765 if (next_row[col] & IN_SIGHT) {
767 * We see this position because of night- or xray-vision.
769 oldseenv = lev->seenv;
770 lev->seenv |= new_angle(lev,sv,row,col); /* update seen angle */
772 /* Update pos if previously not in sight or new angle. */
773 if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
774 newsym(col,row);
777 else if ((next_row[col] & COULD_SEE)
778 && ( (lev->lit && !(HardcoreAlienMode || (ublindf && ublindf->otyp == SHIELD_PATE_GLASSES) || DarkModeBug || autismweaponcheck(ART_PWNHAMMER_DUECE) || autismweaponcheck(ART_LIGHT_____STATED_) || autismweaponcheck(ART_FIRE_EATER) || u.uprops[DARK_MODE_BUG].extrinsic || (uarmf && uarmf->oartifact == ART_BRIGHT_WHITE) || (uarmf && uarmf->oartifact == ART_ENDARKEN_EVERYTHING) || autismweaponcheck(ART_BURGLED_NIGHT_SCYTHE) || have_darkmodestone())) || (next_row[col] & TEMP_LIT))) {
780 * We see this position because it is lit.
782 if ((IS_DOOR(lev->typ) || lev->typ == SDOOR ||
783 IS_WALL(lev->typ)) && (!viz_clear[row][col] || (TezActive && m_at(row,col)) ) ) {
785 * Make sure doors, walls, boulders or mimics don't show up
786 * at the end of dark hallways. We do this by checking
787 * the adjacent position. If it is lit, then we can see
788 * the door or wall, otherwise we can't.
790 dx = u.ux - col; dx = sign(dx);
791 flev = &(levl[col+dx][row+dy]);
792 if ( (flev->lit && !(HardcoreAlienMode || (ublindf && ublindf->otyp == SHIELD_PATE_GLASSES) || DarkModeBug || autismweaponcheck(ART_PWNHAMMER_DUECE) || autismweaponcheck(ART_LIGHT_____STATED_) || autismweaponcheck(ART_FIRE_EATER) || u.uprops[DARK_MODE_BUG].extrinsic || (uarmf && uarmf->oartifact == ART_BRIGHT_WHITE) || (uarmf && uarmf->oartifact == ART_ENDARKEN_EVERYTHING) || autismweaponcheck(ART_BURGLED_NIGHT_SCYTHE) || have_darkmodestone())) || next_array[row+dy][col+dx] & TEMP_LIT) {
793 next_row[col] |= IN_SIGHT; /* we see it */
795 oldseenv = lev->seenv;
796 lev->seenv |= new_angle(lev,sv,row,col);
798 /* Update pos if previously not in sight or new angle.*/
799 if (!(old_row[col] & IN_SIGHT) || oldseenv!=lev->seenv)
800 newsym(col,row);
801 } else
802 goto not_in_sight; /* we don't see it */
804 } else {
805 next_row[col] |= IN_SIGHT; /* we see it */
807 oldseenv = lev->seenv;
808 lev->seenv |= new_angle(lev,sv,row,col);
810 /* Update pos if previously not in sight or new angle. */
811 if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
812 newsym(col,row);
814 } else if ((next_row[col] & COULD_SEE) && lev->waslit) {
816 * If we make it here, the hero _could see_ the location,
817 * but doesn't see it (location is not lit).
818 * However, the hero _remembers_ it as lit (waslit is true).
819 * The hero can now see that it is not lit, so change waslit
820 * and update the location.
822 lev->waslit = 0; /* remember lit condition */
823 newsym(col,row);
826 * At this point we know that the row position is *not* in normal
827 * sight. That is, the position is could be seen, but is dark
828 * or LOS is just plain blocked.
830 * Update the position if:
831 * o If the old one *was* in sight. We may need to clean up
832 * the glyph -- E.g. darken room spot, etc.
833 * o If we now could see the location (yet the location is not
834 * lit), but previously we couldn't see the location, or vice
835 * versa. Update the spot because there there may be an infared
836 * monster there.
838 else {
839 not_in_sight:
840 if ((old_row[col] & IN_SIGHT)
841 || ((next_row[col] & COULD_SEE)
842 ^ (old_row[col] & COULD_SEE)))
843 newsym(col,row);
846 } /* end for col . . */
847 } /* end for row . . */
848 colbump[u.ux] = colbump[u.ux+1] = 0;
850 skip:
851 /* This newsym() caused a crash delivering msg about failure to open
852 * dungeon file init_dungeons() -> panic() -> done(11) ->
853 * vision_recalc(2) -> newsym() -> crash! u.ux and u.uy are 0 and
854 * program_state.panicking == 1 under those circumstances
856 if (!program_state.panicking)
857 newsym(u.ux, u.uy); /* Make sure the hero shows up! */
859 /* Set the new min and max pointers. */
860 viz_rmin = next_rmin;
861 viz_rmax = next_rmax;
866 * block_point()
868 * Make the location opaque to light.
870 void
871 block_point(x,y)
872 int x, y;
874 fill_point(y,x);
876 /* recalc light sources here? */
879 * We have to do a full vision recalculation if we "could see" the
880 * location. Why? Suppose some monster opened a way so that the
881 * hero could see a lit room. However, the position of the opening
882 * was out of night-vision range of the hero. Suddenly the hero should
883 * see the lit room.
885 if (viz_array[y][x]) vision_full_recalc = 1;
889 * unblock_point()
891 * Make the location transparent to light.
893 void
894 unblock_point(x,y)
895 int x, y;
897 dig_point(y,x);
899 /* recalc light sources here? */
901 if (viz_array[y][x]) vision_full_recalc = 1;
904 /* blockorunblock_point() by Amy: test whether the location should be blocked or not, then set it accordingly
905 * used for e.g. terrain-altering effects that put random terrain at locations, because we can't know in advance whether
906 * the terrain it created is blocking vision or not... */
907 void
908 blockorunblock_point(x,y)
909 int x, y;
911 if (does_block(x, y, &levl[x][y])) block_point(x, y);
912 else unblock_point(x, y);
915 /*===========================================================================*\
917 | Everything below this line uses (y,x) instead of (x,y) --- the |
918 | algorithms are faster if they are less recursive and can scan |
919 | on a row longer. |
921 \*===========================================================================*/
924 /* ========================================================================= *\
925 Left and Right Pointer Updates
926 \* ========================================================================= */
929 * LEFT and RIGHT pointer rules
932 * **NOTE** The rules changed on 4/4/90. This comment reflects the
933 * new rules. The change was so that the stone-wall optimization
934 * would work.
936 * OK, now the tough stuff. We must maintain our left and right
937 * row pointers. The rules are as follows:
939 * Left Pointers:
940 * ______________
942 * + If you are a clear spot, your left will point to the first
943 * stone to your left. If there is none, then point the first
944 * legal position in the row (0).
946 * + If you are a blocked spot, then your left will point to the
947 * left-most blocked spot to your left that is connected to you.
948 * This means that a left-edge (a blocked spot that has an open
949 * spot on its left) will point to itself.
952 * Right Pointers:
953 * ---------------
954 * + If you are a clear spot, your right will point to the first
955 * stone to your right. If there is none, then point the last
956 * legal position in the row (COLNO-1).
958 * + If you are a blocked spot, then your right will point to the
959 * right-most blocked spot to your right that is connected to you.
960 * This means that a right-edge (a blocked spot that has an open
961 * spot on its right) will point to itself.
963 STATIC_OVL void
964 dig_point(row,col)
965 int row,col;
967 int i;
969 if (viz_clear[row][col]) return; /* already done */
971 viz_clear[row][col] = 1;
974 * Boundary cases first.
976 if (col == 0) { /* left edge */
977 if (viz_clear[row][1]) {
978 right_ptrs[row][0] = right_ptrs[row][1];
979 } else {
980 right_ptrs[row][0] = 1;
981 for (i = 1; i <= right_ptrs[row][1]; i++)
982 left_ptrs[row][i] = 1;
984 } else if (col == (COLNO-1)) { /* right edge */
986 if (viz_clear[row][COLNO-2]) {
987 left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2];
988 } else {
989 left_ptrs[row][COLNO-1] = COLNO-2;
990 for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++)
991 right_ptrs[row][i] = COLNO-2;
996 * At this point, we know we aren't on the boundaries.
998 else if (viz_clear[row][col-1] && viz_clear[row][col+1]) {
999 /* Both sides clear */
1000 for (i = left_ptrs[row][col-1]; i <= col; i++) {
1001 if (!viz_clear[row][i]) continue; /* catch non-end case */
1002 right_ptrs[row][i] = right_ptrs[row][col+1];
1004 for (i = col; i <= right_ptrs[row][col+1]; i++) {
1005 if (!viz_clear[row][i]) continue; /* catch non-end case */
1006 left_ptrs[row][i] = left_ptrs[row][col-1];
1009 } else if (viz_clear[row][col-1]) {
1010 /* Left side clear, right side blocked. */
1011 for (i = col+1; i <= right_ptrs[row][col+1]; i++)
1012 left_ptrs[row][i] = col+1;
1014 for (i = left_ptrs[row][col-1]; i <= col; i++) {
1015 if (!viz_clear[row][i]) continue; /* catch non-end case */
1016 right_ptrs[row][i] = col+1;
1018 left_ptrs[row][col] = left_ptrs[row][col-1];
1020 } else if (viz_clear[row][col+1]) {
1021 /* Right side clear, left side blocked. */
1022 for (i = left_ptrs[row][col-1]; i < col; i++)
1023 right_ptrs[row][i] = col-1;
1025 for (i = col; i <= right_ptrs[row][col+1]; i++) {
1026 if (!viz_clear[row][i]) continue; /* catch non-end case */
1027 left_ptrs[row][i] = col-1;
1029 right_ptrs[row][col] = right_ptrs[row][col+1];
1031 } else {
1032 /* Both sides blocked */
1033 for (i = left_ptrs[row][col-1]; i < col; i++)
1034 right_ptrs[row][i] = col-1;
1036 for (i = col+1; i <= right_ptrs[row][col+1]; i++)
1037 left_ptrs[row][i] = col+1;
1039 left_ptrs[row][col] = col-1;
1040 right_ptrs[row][col] = col+1;
1044 STATIC_OVL void
1045 fill_point(row,col)
1046 int row, col;
1048 int i;
1050 if (!viz_clear[row][col]) return;
1052 viz_clear[row][col] = 0;
1054 if (col == 0) {
1055 if (viz_clear[row][1]) { /* adjacent is clear */
1056 right_ptrs[row][0] = 0;
1057 } else {
1058 right_ptrs[row][0] = right_ptrs[row][1];
1059 for (i = 1; i <= right_ptrs[row][1]; i++)
1060 left_ptrs[row][i] = 0;
1062 } else if (col == COLNO-1) {
1063 if (viz_clear[row][COLNO-2]) { /* adjacent is clear */
1064 left_ptrs[row][COLNO-1] = COLNO-1;
1065 } else {
1066 left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2];
1067 for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++)
1068 right_ptrs[row][i] = COLNO-1;
1073 * Else we know that we are not on an edge.
1075 else if (viz_clear[row][col-1] && viz_clear[row][col+1]) {
1076 /* Both sides clear */
1077 for (i = left_ptrs[row][col-1]+1; i <= col; i++)
1078 right_ptrs[row][i] = col;
1080 if (!left_ptrs[row][col-1]) /* catch the end case */
1081 right_ptrs[row][0] = col;
1083 for (i = col; i < right_ptrs[row][col+1]; i++)
1084 left_ptrs[row][i] = col;
1086 if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */
1087 left_ptrs[row][COLNO-1] = col;
1089 } else if (viz_clear[row][col-1]) {
1090 /* Left side clear, right side blocked. */
1091 for (i = col; i <= right_ptrs[row][col+1]; i++)
1092 left_ptrs[row][i] = col;
1094 for (i = left_ptrs[row][col-1]+1; i < col; i++)
1095 right_ptrs[row][i] = col;
1097 if (!left_ptrs[row][col-1]) /* catch the end case */
1098 right_ptrs[row][i] = col;
1100 right_ptrs[row][col] = right_ptrs[row][col+1];
1102 } else if (viz_clear[row][col+1]) {
1103 /* Right side clear, left side blocked. */
1104 for (i = left_ptrs[row][col-1]; i <= col; i++)
1105 right_ptrs[row][i] = col;
1107 for (i = col+1; i < right_ptrs[row][col+1]; i++)
1108 left_ptrs[row][i] = col;
1110 if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */
1111 left_ptrs[row][i] = col;
1113 left_ptrs[row][col] = left_ptrs[row][col-1];
1115 } else {
1116 /* Both sides blocked */
1117 for (i = left_ptrs[row][col-1]; i <= col; i++)
1118 right_ptrs[row][i] = right_ptrs[row][col+1];
1120 for (i = col; i <= right_ptrs[row][col+1]; i++)
1121 left_ptrs[row][i] = left_ptrs[row][col-1];
1126 /*===========================================================================*/
1127 /*===========================================================================*/
1128 /* Use either algorithm C or D. See the config.h for more details. =========*/
1131 * Variables local to both Algorithms C and D.
1133 static int start_row;
1134 static int start_col;
1135 static int step;
1136 static char **cs_rows;
1137 static char *cs_left;
1138 static char *cs_right;
1140 static void (*vis_func)(int,int,void *);
1141 static void * varg;
1144 * Both Algorithms C and D use the following macros.
1146 * good_row(z) - Return TRUE if the argument is a legal row.
1147 * set_cs(rowp,col) - Set the local could see array.
1148 * set_min(z) - Save the min value of the argument and the current
1149 * row minimum.
1150 * set_max(z) - Save the max value of the argument and the current
1151 * row maximum.
1153 * The last three macros depend on having local pointers row_min, row_max,
1154 * and rowp being set correctly.
1156 #define set_cs(rowp,col) (rowp[col] = COULD_SEE)
1157 #define good_row(z) ((z) >= 0 && (z) < ROWNO)
1158 #define set_min(z) if (*row_min > (z)) *row_min = (z)
1159 #define set_max(z) if (*row_max < (z)) *row_max = (z)
1160 #define is_clear(row,col) viz_clear_rows[row][col]
1163 * clear_path() expanded into 4 macros/functions:
1165 * q1_path()
1166 * q2_path()
1167 * q3_path()
1168 * q4_path()
1170 * "Draw" a line from the start to the given location. Stop if we hit
1171 * something that blocks light. The start and finish points themselves are
1172 * not checked, just the points between them. These routines do _not_
1173 * expect to be called with the same starting and stopping point.
1175 * These routines use the generalized integer Bresenham's algorithm (fast
1176 * line drawing) for all quadrants. The algorithm was taken from _Procedural
1177 * Elements for Computer Graphics_, by David F. Rogers. McGraw-Hill, 1985.
1179 #ifdef MACRO_CPATH /* quadrant calls are macros */
1182 * When called, the result is in "result".
1183 * The first two arguments (srow,scol) are one end of the path. The next
1184 * two arguments (row,col) are the destination. The last argument is
1185 * used as a C language label. This means that it must be different
1186 * in each pair of calls.
1190 * Quadrant I (step < 0).
1192 #define q1_path(srow,scol,y2,x2,label) \
1194 int dx, dy; \
1195 register int k, err, x, y, dxs, dys; \
1197 x = (scol); y = (srow); \
1198 dx = (x2) - x; dy = y - (y2); \
1200 result = 0; /* default to a blocked path */\
1202 dxs = dx << 1; /* save the shifted values */\
1203 dys = dy << 1; \
1204 if (dy > dx) { \
1205 err = dxs - dy; \
1207 for (k = dy-1; k; k--) { \
1208 if (err >= 0) { \
1209 x++; \
1210 err -= dys; \
1212 y--; \
1213 err += dxs; \
1214 if (!is_clear(y,x)) goto label;/* blocked */\
1216 } else { \
1217 err = dys - dx; \
1219 for (k = dx-1; k; k--) { \
1220 if (err >= 0) { \
1221 y--; \
1222 err -= dxs; \
1224 x++; \
1225 err += dys; \
1226 if (!is_clear(y,x)) goto label;/* blocked */\
1230 result = 1; \
1234 * Quadrant IV (step > 0).
1236 #define q4_path(srow,scol,y2,x2,label) \
1238 int dx, dy; \
1239 register int k, err, x, y, dxs, dys; \
1241 x = (scol); y = (srow); \
1242 dx = (x2) - x; dy = (y2) - y; \
1244 result = 0; /* default to a blocked path */\
1246 dxs = dx << 1; /* save the shifted values */\
1247 dys = dy << 1; \
1248 if (dy > dx) { \
1249 err = dxs - dy; \
1251 for (k = dy-1; k; k--) { \
1252 if (err >= 0) { \
1253 x++; \
1254 err -= dys; \
1256 y++; \
1257 err += dxs; \
1258 if (!is_clear(y,x)) goto label;/* blocked */\
1261 } else { \
1262 err = dys - dx; \
1264 for (k = dx-1; k; k--) { \
1265 if (err >= 0) { \
1266 y++; \
1267 err -= dxs; \
1269 x++; \
1270 err += dys; \
1271 if (!is_clear(y,x)) goto label;/* blocked */\
1275 result = 1; \
1279 * Quadrant II (step < 0).
1281 #define q2_path(srow,scol,y2,x2,label) \
1283 int dx, dy; \
1284 register int k, err, x, y, dxs, dys; \
1286 x = (scol); y = (srow); \
1287 dx = x - (x2); dy = y - (y2); \
1289 result = 0; /* default to a blocked path */\
1291 dxs = dx << 1; /* save the shifted values */\
1292 dys = dy << 1; \
1293 if (dy > dx) { \
1294 err = dxs - dy; \
1296 for (k = dy-1; k; k--) { \
1297 if (err >= 0) { \
1298 x--; \
1299 err -= dys; \
1301 y--; \
1302 err += dxs; \
1303 if (!is_clear(y,x)) goto label;/* blocked */\
1305 } else { \
1306 err = dys - dx; \
1308 for (k = dx-1; k; k--) { \
1309 if (err >= 0) { \
1310 y--; \
1311 err -= dxs; \
1313 x--; \
1314 err += dys; \
1315 if (!is_clear(y,x)) goto label;/* blocked */\
1319 result = 1; \
1323 * Quadrant III (step > 0).
1325 #define q3_path(srow,scol,y2,x2,label) \
1327 int dx, dy; \
1328 register int k, err, x, y, dxs, dys; \
1330 x = (scol); y = (srow); \
1331 dx = x - (x2); dy = (y2) - y; \
1333 result = 0; /* default to a blocked path */\
1335 dxs = dx << 1; /* save the shifted values */\
1336 dys = dy << 1; \
1337 if (dy > dx) { \
1338 err = dxs - dy; \
1340 for (k = dy-1; k; k--) { \
1341 if (err >= 0) { \
1342 x--; \
1343 err -= dys; \
1345 y++; \
1346 err += dxs; \
1347 if (!is_clear(y,x)) goto label;/* blocked */\
1350 } else { \
1351 err = dys - dx; \
1353 for (k = dx-1; k; k--) { \
1354 if (err >= 0) { \
1355 y++; \
1356 err -= dxs; \
1358 x--; \
1359 err += dys; \
1360 if (!is_clear(y,x)) goto label;/* blocked */\
1364 result = 1; \
1367 #else /* quadrants are really functions */
1369 STATIC_DCL int _q1_path(int,int,int,int);
1370 STATIC_DCL int _q2_path(int,int,int,int);
1371 STATIC_DCL int _q3_path(int,int,int,int);
1372 STATIC_DCL int _q4_path(int,int,int,int);
1374 #define q1_path(sy,sx,y,x,dummy) result = _q1_path(sy,sx,y,x)
1375 #define q2_path(sy,sx,y,x,dummy) result = _q2_path(sy,sx,y,x)
1376 #define q3_path(sy,sx,y,x,dummy) result = _q3_path(sy,sx,y,x)
1377 #define q4_path(sy,sx,y,x,dummy) result = _q4_path(sy,sx,y,x)
1380 * Quadrant I (step < 0).
1382 STATIC_OVL int
1383 _q1_path(srow,scol,y2,x2)
1384 int scol, srow, y2, x2;
1386 int dx, dy;
1387 register int k, err, x, y, dxs, dys;
1389 x = scol; y = srow;
1390 dx = x2 - x; dy = y - y2;
1392 dxs = dx << 1; /* save the shifted values */
1393 dys = dy << 1;
1394 if (dy > dx) {
1395 err = dxs - dy;
1397 for (k = dy-1; k; k--) {
1398 if (err >= 0) {
1399 x++;
1400 err -= dys;
1402 y--;
1403 err += dxs;
1404 if (!is_clear(y,x)) return 0; /* blocked */
1406 } else {
1407 err = dys - dx;
1409 for (k = dx-1; k; k--) {
1410 if (err >= 0) {
1411 y--;
1412 err -= dxs;
1414 x++;
1415 err += dys;
1416 if (!is_clear(y,x)) return 0;/* blocked */
1420 return 1;
1424 * Quadrant IV (step > 0).
1426 STATIC_OVL int
1427 _q4_path(srow,scol,y2,x2)
1428 int scol, srow, y2, x2;
1430 int dx, dy;
1431 register int k, err, x, y, dxs, dys;
1433 x = scol; y = srow;
1434 dx = x2 - x; dy = y2 - y;
1436 dxs = dx << 1; /* save the shifted values */
1437 dys = dy << 1;
1438 if (dy > dx) {
1439 err = dxs - dy;
1441 for (k = dy-1; k; k--) {
1442 if (err >= 0) {
1443 x++;
1444 err -= dys;
1446 y++;
1447 err += dxs;
1448 if (!is_clear(y,x)) return 0; /* blocked */
1450 } else {
1451 err = dys - dx;
1453 for (k = dx-1; k; k--) {
1454 if (err >= 0) {
1455 y++;
1456 err -= dxs;
1458 x++;
1459 err += dys;
1460 if (!is_clear(y,x)) return 0;/* blocked */
1464 return 1;
1468 * Quadrant II (step < 0).
1470 STATIC_OVL int
1471 _q2_path(srow,scol,y2,x2)
1472 int scol, srow, y2, x2;
1474 int dx, dy;
1475 register int k, err, x, y, dxs, dys;
1477 x = scol; y = srow;
1478 dx = x - x2; dy = y - y2;
1480 dxs = dx << 1; /* save the shifted values */
1481 dys = dy << 1;
1482 if (dy > dx) {
1483 err = dxs - dy;
1485 for (k = dy-1; k; k--) {
1486 if (err >= 0) {
1487 x--;
1488 err -= dys;
1490 y--;
1491 err += dxs;
1492 if (!is_clear(y,x)) return 0; /* blocked */
1494 } else {
1495 err = dys - dx;
1497 for (k = dx-1; k; k--) {
1498 if (err >= 0) {
1499 y--;
1500 err -= dxs;
1502 x--;
1503 err += dys;
1504 if (!is_clear(y,x)) return 0;/* blocked */
1508 return 1;
1512 * Quadrant III (step > 0).
1514 STATIC_OVL int
1515 _q3_path(srow,scol,y2,x2)
1516 int scol, srow, y2, x2;
1518 int dx, dy;
1519 register int k, err, x, y, dxs, dys;
1521 x = scol; y = srow;
1522 dx = x - x2; dy = y2 - y;
1524 dxs = dx << 1; /* save the shifted values */
1525 dys = dy << 1;
1526 if (dy > dx) {
1527 err = dxs - dy;
1529 for (k = dy-1; k; k--) {
1530 if (err >= 0) {
1531 x--;
1532 err -= dys;
1534 y++;
1535 err += dxs;
1536 if (!is_clear(y,x)) return 0; /* blocked */
1538 } else {
1539 err = dys - dx;
1541 for (k = dx-1; k; k--) {
1542 if (err >= 0) {
1543 y++;
1544 err -= dxs;
1546 x--;
1547 err += dys;
1548 if (!is_clear(y,x)) return 0;/* blocked */
1552 return 1;
1555 #endif /* quadrants are functions */
1558 * Use vision tables to determine if there is a clear path from
1559 * (col1,row1) to (col2,row2). This is used by:
1560 * m_cansee()
1561 * m_canseeu()
1562 * do_light_sources()
1564 boolean
1565 clear_path(col1,row1,col2,row2)
1566 int col1, row1, col2, row2;
1568 int result;
1570 if(col1 < col2) {
1571 if(row1 > row2) {
1572 q1_path(row1,col1,row2,col2,cleardone);
1573 } else {
1574 q4_path(row1,col1,row2,col2,cleardone);
1576 } else {
1577 if(row1 > row2) {
1578 q2_path(row1,col1,row2,col2,cleardone);
1579 } else if(row1 == row2 && col1 == col2) {
1580 result = 1;
1581 } else {
1582 q3_path(row1,col1,row2,col2,cleardone);
1585 #ifdef MACRO_CPATH
1586 cleardone:
1587 #endif
1588 return((boolean)result);
1591 #ifdef VISION_TABLES
1592 /*===========================================================================*\
1593 GENERAL LINE OF SIGHT
1594 Algorithm D
1595 \*===========================================================================*/
1599 * Indicate caller for the shadow routines.
1601 #define FROM_RIGHT 0
1602 #define FROM_LEFT 1
1606 * Include the table definitions.
1608 #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 right_side(int,int,int,int,int,int,int,char*);
1616 STATIC_DCL void left_side(int,int,int,int,int,int,int,char*);
1617 STATIC_DCL int close_shadow(int,int,int,int);
1618 STATIC_DCL int far_shadow(int,int,int,int);
1621 * Initialize algorithm D's table pointers. If we don't have these,
1622 * then we do 3D table lookups. Verrrry slow.
1624 STATIC_OVL void
1625 view_init()
1627 int i;
1629 for (i = 0; i < CLOSE_MAX_BC_DY; i++)
1630 close_dy[i] = &close_table[i];
1632 for (i = 0; i < FAR_MAX_BC_DY; i++)
1633 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) return block_col;
1655 /* Take explicit absolute values. Adjust. */
1656 if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; --sdy; /* src dy */
1657 if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; /* src dx */
1658 if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; /* point dy */
1660 if (sdy < 0 || sdy >= CLOSE_MAX_SB_DY || sdx >= CLOSE_MAX_SB_DX ||
1661 pdy >= CLOSE_MAX_BC_DY) {
1662 impossible("close_shadow: bad value");
1663 return block_col;
1665 offset = close_dy[sdy]->close[sdx][pdy];
1666 if (side == FROM_RIGHT)
1667 return block_col + offset;
1669 return block_col - offset;
1673 STATIC_OVL int
1674 far_shadow(side,this_row,block_row,block_col)
1675 int side,this_row,block_row,block_col;
1677 register int sdy, sdx, pdy, offset;
1680 * Take care of a bug that shows up only on the borders.
1682 * If the block is beyond the border, then the row is negative. Return
1683 * the block's column number (should be 0 or COLNO-1).
1685 * Could easily have the column be -1, but then wouldn't know if it was
1686 * the left or right border.
1688 if (block_row < 0) return block_col;
1690 /* Take explicit absolute values. Adjust. */
1691 if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; /* src dy */
1692 if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; --sdx; /* src dx */
1693 if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; --pdy; /* point dy */
1695 if (sdy >= FAR_MAX_SB_DY || sdx < 0 || sdx >= FAR_MAX_SB_DX ||
1696 pdy < 0 || pdy >= FAR_MAX_BC_DY) {
1697 impossible("far_shadow: bad value");
1698 return block_col;
1700 if ((offset = far_dy[sdy]->far_q[sdx][pdy]) == OFF_TABLE) offset = -1;
1701 if (side == FROM_RIGHT)
1702 return block_col + offset;
1704 return block_col - offset;
1709 * right_side()
1711 * Figure out what could be seen on the right side of the source.
1713 STATIC_OVL void
1714 right_side(row, cb_row, cb_col, fb_row, fb_col, left, right_mark, limits)
1715 int row; /* current row */
1716 int cb_row, cb_col; /* close block row and col */
1717 int fb_row, fb_col; /* far block row and col */
1718 int left; /* left mark of the previous row */
1719 int right_mark; /* right mark of previous row */
1720 char *limits; /* points at range limit for current row, or NULL */
1722 register int i;
1723 register char *rowp;
1724 int hit_stone = 0;
1725 int left_shadow, right_shadow, loc_right;
1726 int lblock_col; /* local block column (current row) */
1727 int nrow, deeper;
1728 char *row_min; /* left most */
1729 char *row_max; /* right most */
1730 int lim_max; /* right most limit of circle */
1732 #ifdef GCC_WARN
1733 rowp = 0;
1734 #endif
1735 nrow = row + step;
1736 #ifdef GCC_WARN
1737 rowp = row_min = row_max = NULL;
1738 lblock_col = 0;
1739 #endif
1740 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
1741 if(!vis_func) {
1742 rowp = cs_rows[row];
1743 row_min = &cs_left[row];
1744 row_max = &cs_right[row];
1746 if(limits) {
1747 lim_max = start_col + *limits;
1748 if(lim_max > COLNO-1) lim_max = COLNO-1;
1749 if(right_mark > lim_max) right_mark = lim_max;
1750 limits++; /* prepare for next row */
1751 } else
1752 lim_max = COLNO-1;
1755 * Get the left shadow from the close block. This value could be
1756 * illegal.
1758 left_shadow = close_shadow(FROM_RIGHT,row,cb_row,cb_col);
1761 * Mark all stone walls as seen before the left shadow. All this work
1762 * for a special case.
1764 * NOTE. With the addition of this code in here, it is now *required*
1765 * for the algorithm to work correctly. If this is commented out,
1766 * change the above assignment so that left and not left_shadow is the
1767 * variable that gets the shadow.
1769 while (left <= right_mark) {
1770 loc_right = right_ptrs[row][left];
1771 if(loc_right > lim_max) loc_right = lim_max;
1772 if (viz_clear_rows[row][left]) {
1773 if (loc_right >= left_shadow) {
1774 left = left_shadow; /* opening ends beyond shadow */
1775 break;
1777 left = loc_right;
1778 loc_right = right_ptrs[row][left];
1779 if(loc_right > lim_max) loc_right = lim_max;
1780 if (left == loc_right) return; /* boundary */
1782 /* Shadow covers opening, beyond right mark */
1783 if (left == right_mark && left_shadow > right_mark) return;
1786 if (loc_right > right_mark) /* can't see stone beyond the mark */
1787 loc_right = right_mark;
1789 if(vis_func) {
1790 for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg);
1791 } else {
1792 for (i = left; i <= loc_right; i++) set_cs(rowp,i);
1793 set_min(left); set_max(loc_right);
1796 if (loc_right == right_mark) return; /* all stone */
1797 if (loc_right >= left_shadow) hit_stone = 1;
1798 left = loc_right + 1;
1802 * At this point we are at the first visible clear spot on or beyond
1803 * the left shadow, unless the left shadow is an illegal value. If we
1804 * have "hit stone" then we have a stone wall just to our left.
1808 * Get the right shadow. Make sure that it is a legal value.
1810 if ((right_shadow = far_shadow(FROM_RIGHT,row,fb_row,fb_col)) >= COLNO)
1811 right_shadow = COLNO-1;
1813 * Make vertical walls work the way we want them. In this case, we
1814 * note when the close block blocks the column just above/beneath
1815 * it (right_shadow < fb_col [actually right_shadow == fb_col-1]). If
1816 * the location is filled, then we want to see it, so we put the
1817 * right shadow back (same as fb_col).
1819 if (right_shadow < fb_col && !viz_clear_rows[row][fb_col])
1820 right_shadow = fb_col;
1821 if(right_shadow > lim_max) right_shadow = lim_max;
1824 * Main loop. Within the range of sight of the previous row, mark all
1825 * stone walls as seen. Follow open areas recursively.
1827 while (left <= right_mark) {
1828 /* Get the far right of the opening or wall */
1829 loc_right = right_ptrs[row][left];
1830 if(loc_right > lim_max) loc_right = lim_max;
1832 if (!viz_clear_rows[row][left]) {
1833 hit_stone = 1; /* use stone on this row as close block */
1835 * We can see all of the wall until the next open spot or the
1836 * start of the shadow caused by the far block (right).
1838 * Can't see stone beyond the right mark.
1840 if (loc_right > right_mark) loc_right = right_mark;
1842 if(vis_func) {
1843 for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg);
1844 } else {
1845 for (i = left; i <= loc_right; i++) set_cs(rowp,i);
1846 set_min(left); set_max(loc_right);
1849 if (loc_right == right_mark) return; /* hit the end */
1850 left = loc_right + 1;
1851 loc_right = right_ptrs[row][left];
1852 if(loc_right > lim_max) loc_right = lim_max;
1853 /* fall through... we know at least one position is visible */
1857 * We are in an opening.
1859 * If this is the first open spot since the could see area (this is
1860 * true if we have hit stone), get the shadow generated by the wall
1861 * just to our left.
1863 if (hit_stone) {
1864 lblock_col = left-1; /* local block column */
1865 left = close_shadow(FROM_RIGHT,row,row,lblock_col);
1866 if (left > lim_max) break; /* off the end */
1870 * Check if the shadow covers the opening. If it does, then
1871 * move to end of the opening. A shadow generated on from a
1872 * wall on this row does *not* cover the wall on the right
1873 * of the opening.
1875 if (left >= loc_right) {
1876 if (loc_right == lim_max) { /* boundary */
1877 if (left == lim_max) {
1878 if(vis_func) (*vis_func)(lim_max, row, varg);
1879 else {
1880 set_cs(rowp,lim_max); /* last pos */
1881 set_max(lim_max);
1884 return; /* done */
1886 left = loc_right;
1887 continue;
1891 * If the far wall of the opening (loc_right) is closer than the
1892 * shadow limit imposed by the far block (right) then use the far
1893 * wall as our new far block when we recurse.
1895 * If the limits are the the same, and the far block really exists
1896 * (fb_row >= 0) then do the same as above.
1898 * Normally, the check would be for the far wall being closer OR EQUAL
1899 * to the shadow limit. However, there is a bug that arises from the
1900 * fact that the clear area pointers end in an open space (if it
1901 * exists) on a boundary. This then makes a far block exist where it
1902 * shouldn't --- on a boundary. To get around that, I had to
1903 * introduce the concept of a non-existent far block (when the
1904 * row < 0). Next I have to check for it. Here is where that check
1905 * exists.
1907 if ((loc_right < right_shadow) ||
1908 (fb_row >= 0 && loc_right == right_shadow)) {
1909 if(vis_func) {
1910 for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg);
1911 } else {
1912 for (i = left; i <= loc_right; i++) set_cs(rowp,i);
1913 set_min(left); set_max(loc_right);
1916 if (deeper) {
1917 if (hit_stone)
1918 right_side(nrow,row,lblock_col,row,loc_right,
1919 left,loc_right,limits);
1920 else
1921 right_side(nrow,cb_row,cb_col,row,loc_right,
1922 left,loc_right,limits);
1926 * The following line, setting hit_stone, is needed for those
1927 * walls that are only 1 wide. If hit stone is *not* set and
1928 * the stone is only one wide, then the close block is the old
1929 * one instead one on the current row. A way around having to
1930 * set it here is to make left = loc_right (not loc_right+1) and
1931 * let the outer loop take care of it. However, if we do that
1932 * then we then have to check for boundary conditions here as
1933 * well.
1935 hit_stone = 1;
1937 left = loc_right+1;
1940 * The opening extends beyond the right mark. This means that
1941 * the next far block is the current far block.
1943 else {
1944 if(vis_func) {
1945 for (i=left; i <= right_shadow; i++) (*vis_func)(i, row, varg);
1946 } else {
1947 for (i = left; i <= right_shadow; i++) set_cs(rowp,i);
1948 set_min(left); set_max(right_shadow);
1951 if (deeper) {
1952 if (hit_stone)
1953 right_side(nrow, row,lblock_col,fb_row,fb_col,
1954 left,right_shadow,limits);
1955 else
1956 right_side(nrow,cb_row, cb_col,fb_row,fb_col,
1957 left,right_shadow,limits);
1960 return; /* we're outta here */
1967 * left_side()
1969 * This routine is the mirror image of right_side(). Please see right_side()
1970 * for blow by blow comments.
1972 STATIC_OVL void
1973 left_side(row, cb_row, cb_col, fb_row, fb_col, left_mark, right, limits)
1974 int row; /* the current row */
1975 int cb_row, cb_col; /* close block row and col */
1976 int fb_row, fb_col; /* far block row and col */
1977 int left_mark; /* left mark of previous row */
1978 int right; /* right mark of the previous row */
1979 char *limits;
1981 register int i;
1982 register char *rowp;
1983 int hit_stone = 0;
1984 int left_shadow, right_shadow, loc_left;
1985 int lblock_col; /* local block column (current row) */
1986 int nrow, deeper;
1987 char *row_min; /* left most */
1988 char *row_max; /* right most */
1989 int lim_min;
1991 #ifdef GCC_WARN
1992 rowp = row_min = row_max = NULL;
1993 lblock_col = 0;
1994 #endif
1995 nrow = row + step;
1996 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
1997 if(!vis_func) {
1998 rowp = cs_rows[row];
1999 row_min = &cs_left[row];
2000 row_max = &cs_right[row];
2002 if(limits) {
2003 lim_min = start_col - *limits;
2004 if(lim_min < 0) lim_min = 0;
2005 if(left_mark < lim_min) left_mark = lim_min;
2006 limits++; /* prepare for next row */
2007 } else
2008 lim_min = 0;
2010 /* This value could be illegal. */
2011 right_shadow = close_shadow(FROM_LEFT,row,cb_row,cb_col);
2013 while ( right >= left_mark ) {
2014 loc_left = left_ptrs[row][right];
2015 if(loc_left < lim_min) loc_left = lim_min;
2016 if (viz_clear_rows[row][right]) {
2017 if (loc_left <= right_shadow) {
2018 right = right_shadow; /* opening ends beyond shadow */
2019 break;
2021 right = loc_left;
2022 loc_left = left_ptrs[row][right];
2023 if(loc_left < lim_min) loc_left = lim_min;
2024 if (right == loc_left) return; /* boundary */
2027 if (loc_left < left_mark) /* can't see beyond the left mark */
2028 loc_left = left_mark;
2030 if(vis_func) {
2031 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
2032 } else {
2033 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
2034 set_min(loc_left); set_max(right);
2037 if (loc_left == left_mark) return; /* all stone */
2038 if (loc_left <= right_shadow) hit_stone = 1;
2039 right = loc_left - 1;
2042 /* At first visible clear spot on or beyond the right shadow. */
2044 if ((left_shadow = far_shadow(FROM_LEFT,row,fb_row,fb_col)) < 0)
2045 left_shadow = 0;
2047 /* Do vertical walls as we want. */
2048 if (left_shadow > fb_col && !viz_clear_rows[row][fb_col])
2049 left_shadow = fb_col;
2050 if(left_shadow < lim_min) left_shadow = lim_min;
2052 while (right >= left_mark) {
2053 loc_left = left_ptrs[row][right];
2055 if (!viz_clear_rows[row][right]) {
2056 hit_stone = 1; /* use stone on this row as close block */
2058 /* We can only see walls until the left mark */
2059 if (loc_left < left_mark) loc_left = left_mark;
2061 if(vis_func) {
2062 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
2063 } else {
2064 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
2065 set_min(loc_left); set_max(right);
2068 if (loc_left == left_mark) return; /* hit end */
2069 right = loc_left - 1;
2070 loc_left = left_ptrs[row][right];
2071 if (loc_left < lim_min) loc_left = lim_min;
2072 /* fall through...*/
2075 /* We are in an opening. */
2076 if (hit_stone) {
2077 lblock_col = right+1; /* stone block (local) */
2078 right = close_shadow(FROM_LEFT,row,row,lblock_col);
2079 if (right < lim_min) return; /* off the end */
2082 /* Check if the shadow covers the opening. */
2083 if (right <= loc_left) {
2084 /* Make a boundary condition work. */
2085 if (loc_left == lim_min) { /* at boundary */
2086 if (right == lim_min) {
2087 if(vis_func) (*vis_func)(lim_min, row, varg);
2088 else {
2089 set_cs(rowp,lim_min); /* caught the last pos */
2090 set_min(lim_min);
2093 return; /* and break out the loop */
2096 right = loc_left;
2097 continue;
2100 /* If the far wall of the opening is closer than the shadow limit. */
2101 if ((loc_left > left_shadow) ||
2102 (fb_row >= 0 && loc_left == left_shadow)) {
2103 if(vis_func) {
2104 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
2105 } else {
2106 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
2107 set_min(loc_left); set_max(right);
2110 if (deeper) {
2111 if (hit_stone)
2112 left_side(nrow,row,lblock_col,row,loc_left,
2113 loc_left,right,limits);
2114 else
2115 left_side(nrow,cb_row,cb_col,row,loc_left,
2116 loc_left,right,limits);
2119 hit_stone = 1; /* needed for walls of width 1 */
2120 right = loc_left-1;
2122 /* The opening extends beyond the left mark. */
2123 else {
2124 if(vis_func) {
2125 for (i=left_shadow; i <= right; i++) (*vis_func)(i, row, varg);
2126 } else {
2127 for (i = left_shadow; i <= right; i++) set_cs(rowp,i);
2128 set_min(left_shadow); set_max(right);
2131 if (deeper) {
2132 if (hit_stone)
2133 left_side(nrow,row,lblock_col,fb_row,fb_col,
2134 left_shadow,right,limits);
2135 else
2136 left_side(nrow,cb_row,cb_col,fb_row,fb_col,
2137 left_shadow,right,limits);
2140 return; /* we're outta here */
2147 * view_from
2149 * Calculate a view from the given location. Initialize and fill a
2150 * ROWNOxCOLNO array (could_see) with all the locations that could be
2151 * seen from the source location. Initialize and fill the left most
2152 * and right most boundaries of what could be seen.
2154 STATIC_OVL void
2155 view_from(srow,scol,loc_cs_rows,left_most,right_most, range, func, arg)
2156 int srow, scol; /* source row and column */
2157 char **loc_cs_rows; /* could_see array (row pointers) */
2158 char *left_most, *right_most; /* limits of what could be seen */
2159 int range; /* 0 if unlimited */
2160 void (*func)(int,int,void *);
2161 void * arg;
2163 register int i;
2164 char *rowp;
2165 int nrow, left, right, left_row, right_row;
2166 char *limits;
2168 /* Set globals for near_shadow(), far_shadow(), etc. to use. */
2169 start_col = scol;
2170 start_row = srow;
2171 cs_rows = loc_cs_rows;
2172 cs_left = left_most;
2173 cs_right = right_most;
2174 vis_func = func;
2175 varg = arg;
2177 /* Find the left and right limits of sight on the starting row. */
2178 if (viz_clear_rows[srow][scol]) {
2179 left = left_ptrs[srow][scol];
2180 right = right_ptrs[srow][scol];
2181 } else {
2182 left = (!scol) ? 0 :
2183 (viz_clear_rows[srow][scol-1] ? left_ptrs[srow][scol-1] : scol-1);
2184 right = (scol == COLNO-1) ? COLNO-1 :
2185 (viz_clear_rows[srow][scol+1] ? right_ptrs[srow][scol+1] : scol+1);
2188 if(range) {
2189 if(range > MAX_RADIUS || range < 1)
2190 panic("view_from called with range %d", range);
2191 limits = circle_ptr(range) + 1; /* start at next row */
2192 if(left < scol - range) left = scol - range;
2193 if(right > scol + range) right = scol + range;
2194 } else
2195 limits = (char*) 0;
2197 if(func) {
2198 for (i = left; i <= right; i++) (*func)(i, srow, arg);
2199 } else {
2200 /* Row optimization */
2201 rowp = cs_rows[srow];
2203 /* We know that we can see our row. */
2204 for (i = left; i <= right; i++) set_cs(rowp,i);
2205 cs_left[srow] = left;
2206 cs_right[srow] = right;
2209 /* The far block has a row number of -1 if we are on an edge. */
2210 right_row = (right == COLNO-1) ? -1 : srow;
2211 left_row = (!left) ? -1 : srow;
2214 * Check what could be seen in quadrants.
2216 if ( (nrow = srow+1) < ROWNO ) {
2217 step = 1; /* move down */
2218 if (scol<COLNO-1)
2219 right_side(nrow,-1,scol,right_row,right,scol,right,limits);
2220 if (scol)
2221 left_side(nrow,-1,scol,left_row, left, left, scol,limits);
2224 if ( (nrow = srow-1) >= 0 ) {
2225 step = -1; /* move up */
2226 if (scol<COLNO-1)
2227 right_side(nrow,-1,scol,right_row,right,scol,right,limits);
2228 if (scol)
2229 left_side(nrow,-1,scol,left_row, left, left, scol,limits);
2234 #else /*===== End of algorithm D =====*/
2237 /*===========================================================================*\
2238 GENERAL LINE OF SIGHT
2239 Algorithm C
2240 \*===========================================================================*/
2243 * Defines local to Algorithm C.
2245 STATIC_DCL void right_side(int,int,int,char*);
2246 STATIC_DCL void left_side(int,int,int,char*);
2248 /* Initialize algorithm C (nothing). */
2249 STATIC_OVL void
2250 view_init()
2255 * Mark positions as visible on one quadrant of the right side. The
2256 * quadrant is determined by the value of the global variable step.
2258 STATIC_OVL void
2259 right_side(row, left, right_mark, limits)
2260 int row; /* current row */
2261 int left; /* first (left side) visible spot on prev row */
2262 int right_mark; /* last (right side) visible spot on prev row */
2263 char *limits; /* points at range limit for current row, or NULL */
2265 int right; /* right limit of "could see" */
2266 int right_edge; /* right edge of an opening */
2267 int nrow; /* new row (calculate once) */
2268 int deeper; /* if TRUE, call self as needed */
2269 int result; /* set by q?_path() */
2270 register int i; /* loop counter */
2271 register char *rowp; /* row optimization */
2272 char *row_min; /* left most [used by macro set_min()] */
2273 char *row_max; /* right most [used by macro set_max()] */
2274 int lim_max; /* right most limit of circle */
2276 #ifdef GCC_WARN
2277 rowp = row_min = row_max = 0;
2278 #endif
2279 nrow = row + step;
2281 * Can go deeper if the row is in bounds and the next row is within
2282 * the circle's limit. We tell the latter by checking to see if the next
2283 * limit value is the start of a new circle radius (meaning we depend
2284 * on the structure of circle_data[]).
2286 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
2287 if(!vis_func) {
2288 rowp = cs_rows[row]; /* optimization */
2289 row_min = &cs_left[row];
2290 row_max = &cs_right[row];
2292 if(limits) {
2293 lim_max = start_col + *limits;
2294 if(lim_max > COLNO-1) lim_max = COLNO-1;
2295 if(right_mark > lim_max) right_mark = lim_max;
2296 limits++; /* prepare for next row */
2297 } else
2298 lim_max = COLNO-1;
2300 while (left <= right_mark) {
2301 right_edge = right_ptrs[row][left];
2302 if(right_edge > lim_max) right_edge = lim_max;
2304 if (!is_clear(row,left)) {
2306 * Jump to the far side of a stone wall. We can set all
2307 * the points in between as seen.
2309 * If the right edge goes beyond the right mark, check to see
2310 * how much we can see.
2312 if (right_edge > right_mark) {
2314 * If the mark on the previous row was a clear position,
2315 * the odds are that we can actually see part of the wall
2316 * beyond the mark on this row. If so, then see one beyond
2317 * the mark. Otherwise don't. This is a kludge so corners
2318 * with an adjacent doorway show up in nethack.
2320 right_edge = is_clear(row-step,right_mark) ?
2321 right_mark+1 : right_mark;
2323 if(vis_func) {
2324 for (i = left; i <= right_edge; i++) (*vis_func)(i, row, varg);
2325 } else {
2326 for (i = left; i <= right_edge; i++) set_cs(rowp,i);
2327 set_min(left); set_max(right_edge);
2329 left = right_edge + 1; /* no limit check necessary */
2330 continue;
2333 /* No checking needed if our left side is the start column. */
2334 if (left != start_col) {
2336 * Find the left side. Move right until we can see it or we run
2337 * into a wall.
2339 for (; left <= right_edge; left++) {
2340 if (step < 0) {
2341 q1_path(start_row,start_col,row,left,rside1);
2342 } else {
2343 q4_path(start_row,start_col,row,left,rside1);
2345 rside1: /* used if q?_path() is a macro */
2346 if (result) break;
2350 * Check for boundary conditions. We *need* check (2) to break
2351 * an infinite loop where:
2353 * left == right_edge == right_mark == lim_max.
2356 if (left > lim_max) return; /* check (1) */
2357 if (left == lim_max) { /* check (2) */
2358 if(vis_func) (*vis_func)(lim_max, row, varg);
2359 else {
2360 set_cs(rowp,lim_max);
2361 set_max(lim_max);
2363 return;
2366 * Check if we can see any spots in the opening. We might
2367 * (left == right_edge) or might not (left == right_edge+1) have
2368 * been able to see the far wall. Make sure we *can* see the
2369 * wall (remember, we can see the spot above/below this one)
2370 * by backing up.
2372 if (left >= right_edge) {
2373 left = right_edge; /* for the case left == right_edge+1 */
2374 continue;
2379 * Find the right side. If the marker from the previous row is
2380 * closer than the edge on this row, then we have to check
2381 * how far we can see around the corner (under the overhang). Stop
2382 * at the first non-visible spot or we actually hit the far wall.
2384 * Otherwise, we know we can see the right edge of the current row.
2386 * This must be a strict less than so that we can always see a
2387 * horizontal wall, even if it is adjacent to us.
2389 if (right_mark < right_edge) {
2390 for (right = right_mark; right <= right_edge; right++) {
2391 if (step < 0) {
2392 q1_path(start_row,start_col,row,right,rside2);
2393 } else {
2394 q4_path(start_row,start_col,row,right,rside2);
2396 rside2: /* used if q?_path() is a macro */
2397 if (!result) break;
2399 --right; /* get rid of the last increment */
2401 else
2402 right = right_edge;
2405 * We have the range that we want. Set the bits. Note that
2406 * there is no else --- we no longer handle splinters.
2408 if (left <= right) {
2410 * An ugly special case. If you are adjacent to a vertical wall
2411 * and it has a break in it, then the right mark is set to be
2412 * start_col. We *want* to be able to see adjacent vertical
2413 * walls, so we have to set it back.
2415 if (left == right && left == start_col &&
2416 start_col < (COLNO-1) && !is_clear(row,start_col+1))
2417 right = start_col+1;
2419 if(right > lim_max) right = lim_max;
2420 /* set the bits */
2421 if(vis_func)
2422 for (i = left; i <= right; i++) (*vis_func)(i, row, varg);
2423 else {
2424 for (i = left; i <= right; i++) set_cs(rowp,i);
2425 set_min(left); set_max(right);
2428 /* recursive call for next finger of light */
2429 if (deeper) right_side(nrow,left,right,limits);
2430 left = right + 1; /* no limit check necessary */
2437 * This routine is the mirror image of right_side(). See right_side() for
2438 * extensive comments.
2440 STATIC_OVL void
2441 left_side(row, left_mark, right, limits)
2442 int row, left_mark, right;
2443 char *limits;
2445 int left, left_edge, nrow, deeper, result;
2446 register int i;
2447 register char *rowp;
2448 char *row_min, *row_max;
2449 int lim_min;
2451 #ifdef GCC_WARN
2452 rowp = row_min = row_max = 0;
2453 #endif
2454 nrow = row+step;
2455 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
2456 if(!vis_func) {
2457 rowp = cs_rows[row];
2458 row_min = &cs_left[row];
2459 row_max = &cs_right[row];
2461 if(limits) {
2462 lim_min = start_col - *limits;
2463 if(lim_min < 0) lim_min = 0;
2464 if(left_mark < lim_min) left_mark = lim_min;
2465 limits++; /* prepare for next row */
2466 } else
2467 lim_min = 0;
2469 while (right >= left_mark) {
2470 left_edge = left_ptrs[row][right];
2471 if(left_edge < lim_min) left_edge = lim_min;
2473 if (!is_clear(row,right)) {
2474 /* Jump to the far side of a stone wall. */
2475 if (left_edge < left_mark) {
2476 /* Maybe see more (kludge). */
2477 left_edge = is_clear(row-step,left_mark) ?
2478 left_mark-1 : left_mark;
2480 if(vis_func) {
2481 for (i = left_edge; i <= right; i++) (*vis_func)(i, row, varg);
2482 } else {
2483 for (i = left_edge; i <= right; i++) set_cs(rowp,i);
2484 set_min(left_edge); set_max(right);
2486 right = left_edge - 1; /* no limit check necessary */
2487 continue;
2490 if (right != start_col) {
2491 /* Find the right side. */
2492 for (; right >= left_edge; right--) {
2493 if (step < 0) {
2494 q2_path(start_row,start_col,row,right,lside1);
2495 } else {
2496 q3_path(start_row,start_col,row,right,lside1);
2498 lside1: /* used if q?_path() is a macro */
2499 if (result) break;
2502 /* Check for boundary conditions. */
2503 if (right < lim_min) return;
2504 if (right == lim_min) {
2505 if(vis_func) (*vis_func)(lim_min, row, varg);
2506 else {
2507 set_cs(rowp,lim_min);
2508 set_min(lim_min);
2510 return;
2512 /* Check if we can see any spots in the opening. */
2513 if (right <= left_edge) {
2514 right = left_edge;
2515 continue;
2519 /* Find the left side. */
2520 if (left_mark > left_edge) {
2521 for (left = left_mark; left >= left_edge; --left) {
2522 if (step < 0) {
2523 q2_path(start_row,start_col,row,left,lside2);
2524 } else {
2525 q3_path(start_row,start_col,row,left,lside2);
2527 lside2: /* used if q?_path() is a macro */
2528 if (!result) break;
2530 left++; /* get rid of the last decrement */
2532 else
2533 left = left_edge;
2535 if (left <= right) {
2536 /* An ugly special case. */
2537 if (left == right && right == start_col &&
2538 start_col > 0 && !is_clear(row,start_col-1))
2539 left = start_col-1;
2541 if(left < lim_min) left = lim_min;
2542 if(vis_func)
2543 for (i = left; i <= right; i++) (*vis_func)(i, row, varg);
2544 else {
2545 for (i = left; i <= right; i++) set_cs(rowp,i);
2546 set_min(left); set_max(right);
2549 /* Recurse */
2550 if (deeper) left_side(nrow,left,right,limits);
2551 right = left - 1; /* no limit check necessary */
2558 * Calculate all possible visible locations from the given location
2559 * (srow,scol). NOTE this is (y,x)! Mark the visible locations in the
2560 * array provided.
2562 STATIC_OVL void
2563 view_from(srow, scol, loc_cs_rows, left_most, right_most, range, func, arg)
2564 int srow, scol; /* starting row and column */
2565 char **loc_cs_rows; /* pointers to the rows of the could_see array */
2566 char *left_most; /* min mark on each row */
2567 char *right_most; /* max mark on each row */
2568 int range; /* 0 if unlimited */
2569 void (*func)(int,int,void *);
2570 void * arg;
2572 register int i; /* loop counter */
2573 char *rowp; /* optimization for setting could_see */
2574 int nrow; /* the next row */
2575 int left; /* the left-most visible column */
2576 int right; /* the right-most visible column */
2577 char *limits; /* range limit for next row */
2579 /* Set globals for q?_path(), left_side(), and right_side() to use. */
2580 start_col = scol;
2581 start_row = srow;
2582 cs_rows = loc_cs_rows; /* 'could see' rows */
2583 cs_left = left_most;
2584 cs_right = right_most;
2585 vis_func = func;
2586 varg = arg;
2589 * Determine extent of sight on the starting row.
2591 if (is_clear(srow,scol)) {
2592 left = left_ptrs[srow][scol];
2593 right = right_ptrs[srow][scol];
2594 } else {
2596 * When in stone, you can only see your adjacent squares, unless
2597 * you are on an array boundary or a stone/clear boundary.
2599 left = (!scol) ? 0 :
2600 (is_clear(srow,scol-1) ? left_ptrs[srow][scol-1] : scol-1);
2601 right = (scol == COLNO-1) ? COLNO-1 :
2602 (is_clear(srow,scol+1) ? right_ptrs[srow][scol+1] : scol+1);
2605 if(range) {
2606 if(range > MAX_RADIUS || range < 1)
2607 panic("view_from called with range %d", range);
2608 limits = circle_ptr(range) + 1; /* start at next row */
2609 if(left < scol - range) left = scol - range;
2610 if(right > scol + range) right = scol + range;
2611 } else
2612 limits = (char*) 0;
2614 if(func) {
2615 for (i = left; i <= right; i++) (*func)(i, srow, arg);
2616 } else {
2617 /* Row pointer optimization. */
2618 rowp = cs_rows[srow];
2620 /* We know that we can see our row. */
2621 for (i = left; i <= right; i++) set_cs(rowp,i);
2622 cs_left[srow] = left;
2623 cs_right[srow] = right;
2627 * Check what could be seen in quadrants. We need to check for valid
2628 * rows here, since we don't do it in the routines right_side() and
2629 * left_side() [ugliness to remove extra routine calls].
2631 if ( (nrow = srow+1) < ROWNO ) { /* move down */
2632 step = 1;
2633 if (scol < COLNO-1) right_side(nrow, scol, right, limits);
2634 if (scol) left_side (nrow, left, scol, limits);
2637 if ( (nrow = srow-1) >= 0 ) { /* move up */
2638 step = -1;
2639 if (scol < COLNO-1) right_side(nrow, scol, right, limits);
2640 if (scol) left_side (nrow, left, scol, limits);
2644 #endif /*===== End of algorithm C =====*/
2647 * AREA OF EFFECT "ENGINE"
2649 * Calculate all possible visible locations as viewed from the given location
2650 * (srow,scol) within the range specified. Perform "func" with (x, y) args and
2651 * additional argument "arg" for each square.
2653 * If not centered on the hero, just forward arguments to view_from(); it
2654 * will call "func" when necessary. If the hero is the center, use the
2655 * vision matrix and reduce extra work.
2657 void
2658 do_clear_area(scol,srow,range,func,arg)
2659 int scol, srow, range;
2660 void (*func)(int,int,void *);
2661 void * arg;
2663 /* If not centered on hero, do the hard work of figuring the area */
2664 if (scol != u.ux || srow != u.uy)
2665 view_from(srow, scol, (char **)0, (char *)0, (char *)0,
2666 range, func, arg);
2667 else {
2668 register int x;
2669 int y, min_x, max_x, max_y, offset;
2670 char *limits;
2672 if (range > MAX_RADIUS || range < 1)
2673 panic("do_clear_area: illegal range %d", range);
2674 if(vision_full_recalc)
2675 vision_recalc(0); /* recalc vision if dirty */
2676 limits = circle_ptr(range);
2677 if ((max_y = (srow + range)) >= ROWNO) max_y = ROWNO-1;
2678 if ((y = (srow - range)) < 0) y = 0;
2679 for (; y <= max_y; y++) {
2680 offset = limits[v_abs(y-srow)];
2681 if((min_x = (scol - offset)) < 0) min_x = 0;
2682 if((max_x = (scol + offset)) >= COLNO) max_x = COLNO-1;
2683 for (x = min_x; x <= max_x; x++)
2684 if (couldsee(x, y))
2685 (*func)(x, y, arg);
2690 void
2691 do_clear_areaX(scol,srow,range,func,arg) /* cloned function that does not use sight */
2692 int scol, srow, range;
2693 void (*func)(int,int,void *);
2694 void * arg;
2696 /* If not centered on hero, do the hard work of figuring the area */
2697 if (scol != u.ux || srow != u.uy)
2698 view_from(srow, scol, (char **)0, (char *)0, (char *)0,
2699 range, func, arg);
2700 else {
2701 register int x;
2702 int y, min_x, max_x, max_y, offset;
2703 char *limits;
2705 if (range > MAX_RADIUS || range < 1)
2706 panic("do_clear_area: illegal range %d", range);
2707 if(vision_full_recalc)
2708 vision_recalc(0); /* recalc vision if dirty */
2709 limits = circle_ptr(range);
2710 if ((max_y = (srow + range)) >= ROWNO) max_y = ROWNO-1;
2711 if ((y = (srow - range)) < 0) y = 0;
2712 for (; y <= max_y; y++) {
2713 offset = limits[v_abs(y-srow)];
2714 if((min_x = (scol - offset)) < 0) min_x = 0;
2715 if((max_x = (scol + offset)) >= COLNO) max_x = COLNO-1;
2716 for (x = min_x; x <= max_x; x++)
2717 /*if (couldsee(x, y))*/
2718 (*func)(x, y, arg);
2723 /*vision.c*/