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. */
4
5 #include "hack.h"
6
7 /* Circles ==================================================================*/
8
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:
17 *
18 * XXX +2
19 * ...X +3
20 * ....X +4
21 * ....X +4
22 * @...X +4
23 *
24 */
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 */
42 };
43
44 /*
45 * These are the starting indexes into the circle_data[] array for a
46 * circle of a given radius.
47 */
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,
65 };
66
67
68 /*===========================================================================*/
69 /* Vision (arbitrary line of sight) =========================================*/
70
71 /*------ global variables ------*/
72
73 #if 0 /* (moved to decl.c) */
74 /* True if we need to run a full vision recalculation. */
75 boolean vision_full_recalc = 0;
76
77 /* Pointers to the current vision array. */
78 char **viz_array;
79 #endif
80 char *viz_rmin, *viz_rmax; /* current vision cs bounds */
81
82
83 /*------ local variables ------*/
84
85
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];
90
91 static char viz_clear[ROWNO][COLNO]; /* vision clear/blocked map */
92 static char *viz_clear_rows[ROWNO];
93
94 static char left_ptrs[ROWNO][COLNO]; /* LOS algorithm helpers */
95 static char right_ptrs[ROWNO][COLNO];
96
97 /* Forward declarations. */
98 STATIC_DCL void FDECL(fill_point, (int,int));
99 STATIC_DCL void FDECL(dig_point, (int,int));
100 STATIC_DCL void NDECL(view_init);
101 STATIC_DCL void FDECL(view_from,(int,int,char **,char *,char *,int,
102 void (*)(int,int,genericptr_t),genericptr_t));
103 STATIC_DCL void FDECL(get_unused_cs, (char ***,char **,char **));
104 #ifdef REINCARNATION
105 STATIC_DCL void FDECL(rogue_vision, (char **,char *,char *));
106 #endif
107
108 /* Macro definitions that I can't find anywhere. */
109 #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0 ))
110 #define v_abs(z) ((z) < 0 ? -(z) : (z)) /* don't use abs -- it may exist */
111
112 /*
113 * vision_init()
114 *
115 * The one-time vision initialization routine.
116 *
117 * This must be called before mklev() is called in newgame() [allmain.c],
118 * or before a game restore. Else we die a horrible death.
119 */
120 void
vision_init()121 vision_init()
122 {
123 int i;
124
125 /* Set up the pointers. */
126 for (i = 0; i < ROWNO; i++) {
127 cs_rows0[i] = could_see[0][i];
128 cs_rows1[i] = could_see[1][i];
129 viz_clear_rows[i] = viz_clear[i];
130 }
131
132 /* Start out with cs0 as our current array */
133 viz_array = cs_rows0;
134 viz_rmin = cs_rmin0;
135 viz_rmax = cs_rmax0;
136
137 vision_full_recalc = 0;
138 (void) memset((genericptr_t) could_see, 0, sizeof(could_see));
139
140 /* Initialize the vision algorithm (currently C or D). */
141 view_init();
142
143 #ifdef VISION_TABLES
144 /* Note: this initializer doesn't do anything except guarantee that
145 we're linked properly.
146 */
147 vis_tab_init();
148 #endif
149 }
150
151 /*
152 * does_block()
153 *
154 * Returns true if the level feature, object, or monster at (x,y) blocks
155 * sight.
156 */
157 int
does_block(x,y,lev)158 does_block(x,y,lev)
159 int x, y;
160 register struct rm *lev;
161 {
162 struct obj *obj;
163 struct monst *mon;
164
165 /* Features that block . . */
166 if (IS_ROCK(lev->typ) || lev->typ == TREE || (IS_DOOR(lev->typ) &&
167 (lev->doormask & (D_CLOSED|D_LOCKED|D_TRAPPED) )))
168 return 1;
169
170 if (lev->typ == CLOUD || lev->typ == WATER ||
171 (lev->typ == MOAT && Underwater))
172 return 1;
173
174 /* Boulders block light. */
175 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
176 if (obj->otyp == BOULDER) return 1;
177
178 /* Mimics mimicing a door or boulder block light. */
179 if ((mon = m_at(x,y)) && (!mon->minvis || See_invisible) &&
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;
184
185 return 0;
186 }
187
188 /*
189 * vision_reset()
190 *
191 * This must be called *after* the levl[][] structure is set with the new
192 * level and the level monsters and objects are in place.
193 */
194 void
vision_reset()195 vision_reset()
196 {
197 int y;
198 register int x, i, dig_left, block;
199 register struct rm *lev;
200
201 /* Start out with cs0 as our current array */
202 viz_array = cs_rows0;
203 viz_rmin = cs_rmin0;
204 viz_rmax = cs_rmax0;
205
206 (void) memset((genericptr_t) could_see, 0, sizeof(could_see));
207
208 /* Reset the pointers and clear so that we have a "full" dungeon. */
209 (void) memset((genericptr_t) viz_clear, 0, sizeof(viz_clear));
210
211 /* Dig the level */
212 for (y = 0; y < ROWNO; y++) {
213 dig_left = 0;
214 block = TRUE; /* location (0,y) is always stone; it's !isok() */
215 lev = &levl[1][y];
216 for (x = 1; x < COLNO; x++, lev += ROWNO)
217 if (block != (IS_ROCK(lev->typ) || does_block(x,y,lev))) {
218 if(block) {
219 for(i=dig_left; i<x; i++) {
220 left_ptrs [y][i] = dig_left;
221 right_ptrs[y][i] = x-1;
222 }
223 } else {
224 i = dig_left;
225 if(dig_left) dig_left--; /* point at first blocked point */
226 for(; i<x; i++) {
227 left_ptrs [y][i] = dig_left;
228 right_ptrs[y][i] = x;
229 viz_clear[y][i] = 1;
230 }
231 }
232 dig_left = x;
233 block = !block;
234 }
235 /* handle right boundary; almost identical for blocked/unblocked */
236 i = dig_left;
237 if(!block && dig_left) 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;
242 }
243 }
244
245 iflags.vision_inited = 1; /* vision is ready */
246 vision_full_recalc = 1; /* we want to run vision_recalc() */
247 }
248
249
250 /*
251 * get_unused_cs()
252 *
253 * Called from vision_recalc() and at least one light routine. Get pointers
254 * to the unused vision work area.
255 */
256 STATIC_OVL void
get_unused_cs(rows,rmin,rmax)257 get_unused_cs(rows, rmin, rmax)
258 char ***rows;
259 char **rmin, **rmax;
260 {
261 register int row;
262 register char *nrmin, *nrmax;
263
264 if (viz_array == cs_rows0) {
265 *rows = cs_rows1;
266 *rmin = cs_rmin1;
267 *rmax = cs_rmax1;
268 } else {
269 *rows = cs_rows0;
270 *rmin = cs_rmin0;
271 *rmax = cs_rmax0;
272 }
273
274 /* return an initialized, unused work area */
275 nrmin = *rmin;
276 nrmax = *rmax;
277
278 (void) memset((genericptr_t)**rows, 0, ROWNO*COLNO); /* we see nothing */
279 for (row = 0; row < ROWNO; row++) { /* set row min & max */
280 *nrmin++ = COLNO-1;
281 *nrmax++ = 0;
282 }
283 }
284
285
286 #ifdef REINCARNATION
287 /*
288 * rogue_vision()
289 *
290 * Set the "could see" and in sight bits so vision acts just like the old
291 * rogue game:
292 *
293 * + If in a room, the hero can see to the room boundaries.
294 * + The hero can always see adjacent squares.
295 *
296 * We set the in_sight bit here as well to escape a bug that shows up
297 * due to the one-sided lit wall hack.
298 */
299 STATIC_OVL void
rogue_vision(next,rmin,rmax)300 rogue_vision(next, rmin, rmax)
301 char **next; /* could_see array pointers */
302 char *rmin, *rmax;
303 {
304 int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET; /* no SHARED... */
305 int start, stop, in_door, xhi, xlo, yhi, ylo;
306 register int zx, zy;
307
308 /* If in a lit room, we are able to see to its boundaries. */
309 /* If dark, set COULD_SEE so various spells work -dlc */
310 if (rnum >= 0) {
311 for (zy = rooms[rnum].ly-1; zy <= rooms[rnum].hy+1; zy++) {
312 rmin[zy] = start = rooms[rnum].lx-1;
313 rmax[zy] = stop = rooms[rnum].hx+1;
314
315 for (zx = start; zx <= stop; zx++) {
316 if (rooms[rnum].rlit) {
317 next[zy][zx] = COULD_SEE | IN_SIGHT;
318 levl[zx][zy].seenv = SVALL; /* see the walls */
319 } else
320 next[zy][zx] = COULD_SEE;
321 }
322 }
323 }
324
325 in_door = levl[u.ux][u.uy].typ == DOOR;
326
327 /* Can always see adjacent. */
328 ylo = max(u.uy - 1, 0);
329 yhi = min(u.uy + 1, ROWNO - 1);
330 xlo = max(u.ux - 1, 1);
331 xhi = min(u.ux + 1, COLNO - 1);
332 for (zy = ylo; zy <= yhi; zy++) {
333 if (xlo < rmin[zy]) rmin[zy] = xlo;
334 if (xhi > rmax[zy]) rmax[zy] = xhi;
335
336 for (zx = xlo; zx <= xhi; zx++) {
337 next[zy][zx] = COULD_SEE | IN_SIGHT;
338 /*
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.
345 */
346 if (in_door && (zx == u.ux || zy == u.uy)) newsym(zx,zy);
347 }
348 }
349 }
350 #endif /* REINCARNATION */
351
352 /*#define EXTEND_SPINE*/ /* possibly better looking wall-angle */
353
354 #ifdef EXTEND_SPINE
355
356 STATIC_DCL int FDECL(new_angle, (struct rm *, unsigned char *, int, int));
357 /*
358 * new_angle()
359 *
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.
362 *
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:
368 *
369 * Correct, but ugly Extend T spine
370 *
371 * | ... | ...
372 * | ... <-- wall beyond & floor --> | ...
373 * | ... | ...
374 * Unseen --> ... | ...
375 * spine +-... <-- trwall & doorway --> +-...
376 * | ... | ...
377 *
378 *
379 * @ <-- hero --> @
380 *
381 *
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.
386 *
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.
393 *
394 *
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
398 */
399 STATIC_OVL int
new_angle(lev,sv,row,col)400 new_angle(lev, sv, row, col)
401 struct rm *lev;
402 unsigned char *sv;
403 int row, col;
404 {
405 register int res = *sv;
406
407 /*
408 * Do extra checks for crosswalls and T walls if we see them from
409 * an angle.
410 */
411 if (lev->typ >= CROSSWALL && lev->typ <= TRWALL) {
412 switch (res) {
413 case SV0:
414 if (col > 0 && viz_clear[row][col-1]) res |= SV7;
415 if (row > 0 && viz_clear[row-1][col]) res |= SV1;
416 break;
417 case SV2:
418 if (row > 0 && viz_clear[row-1][col]) res |= SV1;
419 if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3;
420 break;
421 case SV4:
422 if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3;
423 if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5;
424 break;
425 case SV6:
426 if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5;
427 if (col > 0 && viz_clear[row][col-1]) res |= SV7;
428 break;
429 }
430 }
431 return res;
432 }
433 #else
434 /*
435 * new_angle()
436 *
437 * Return the new angle seen by the hero for this location. The angle
438 * bit is given in the value pointed at by sv.
439 *
440 * The other parameters are not used.
441 */
442 #define new_angle(lev, sv, row, col) (*sv)
443
444 #endif
445
446
447 /*
448 * vision_recalc()
449 *
450 * Do all of the heavy vision work. Recalculate all locations that could
451 * possibly be seen by the hero --- if the location were lit, etc. Note
452 * which locations are actually seen because of lighting. Then add to
453 * this all locations that be seen by hero due to night vision and x-ray
454 * vision. Finally, compare with what the hero was able to see previously.
455 * Update the difference.
456 *
457 * This function is usually called only when the variable 'vision_full_recalc'
458 * is set. The following is a list of places where this function is called,
459 * with three valid values for the control flag parameter:
460 *
461 * Control flag = 0. A complete vision recalculation. Generate the vision
462 * tables from scratch. This is necessary to correctly set what the hero
463 * can see. (1) and (2) call this routine for synchronization purposes, (3)
464 * calls this routine so it can operate correctly.
465 *
466 * + After the monster move, before input from the player. [moveloop()]
467 * + At end of moveloop. [moveloop() ??? not sure why this is here]
468 * + Right before something is printed. [pline()]
469 * + Right before we do a vision based operation. [do_clear_area()]
470 * + screen redraw, so we can renew all positions in sight. [docrt()]
471 *
472 * Control flag = 1. An adjacent vision recalculation. The hero has moved
473 * one square. Knowing this, it might be possible to optimize the vision
474 * recalculation using the current knowledge. This is presently unimplemented
475 * and is treated as a control = 0 call.
476 *
477 * + Right after the hero moves. [domove()]
478 *
479 * Control flag = 2. Turn off the vision system. Nothing new will be
480 * displayed, since nothing is seen. This is usually done when you need
481 * a newsym() run on all locations in sight, or on some locations but you
482 * don't know which ones.
483 *
484 * + Before a screen redraw, so all positions are renewed. [docrt()]
485 * + Right before the hero arrives on a new level. [goto_level()]
486 * + Right after a scroll of light is read. [litroom()]
487 * + After an option has changed that affects vision [parseoptions()]
488 * + Right after the hero is swallowed. [gulpmu()]
489 * + Just before bubbles are moved. [movebubbles()]
490 */
491 void
vision_recalc(control)492 vision_recalc(control)
493 int control;
494 {
495 char **temp_array; /* points to the old vision array */
496 char **next_array; /* points to the new vision array */
497 char *next_row; /* row pointer for the new array */
498 char *old_row; /* row pointer for the old array */
499 char *next_rmin; /* min pointer for the new array */
500 char *next_rmax; /* max pointer for the new array */
501 char *ranges; /* circle ranges -- used for xray & night vision */
502 int row; /* row counter (outer loop) */
503 int start, stop; /* inner loop starting/stopping index */
504 int dx, dy; /* one step from a lit door or lit wall (see below) */
505 register int col; /* inner loop counter */
506 register struct rm *lev; /* pointer to current pos */
507 struct rm *flev; /* pointer to position in "front" of current pos */
508 extern unsigned char seenv_matrix[3][3]; /* from display.c */
509 static unsigned char colbump[COLNO+1]; /* cols to bump sv */
510 unsigned char *sv; /* ptr to seen angle bits */
511 int oldseenv; /* previous seenv value */
512
513 vision_full_recalc = 0; /* reset flag */
514 if (in_mklev || !iflags.vision_inited) return;
515
516 #ifdef GCC_WARN
517 row = 0;
518 #endif
519
520 /*
521 * Either the light sources have been taken care of, or we must
522 * recalculate them here.
523 */
524
525 /* Get the unused could see, row min, and row max arrays. */
526 get_unused_cs(&next_array, &next_rmin, &next_rmax);
527
528 /* You see nothing, nothing can see you --- if swallowed or refreshing. */
529 if (u.uswallow || control == 2) {
530 /* do nothing -- get_unused_cs() nulls out the new work area */
531
532 } else if (Blind) {
533 /*
534 * Calculate the could_see array even when blind so that monsters
535 * can see you, even if you can't see them. Note that the current
536 * setup allows:
537 *
538 * + Monsters to see with the "new" vision, even on the rogue
539 * level.
540 *
541 * + Monsters can see you even when you're in a pit.
542 */
543 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax,
544 0, (void FDECL((*),(int,int,genericptr_t)))0, (genericptr_t)0);
545
546 /*
547 * Our own version of the update loop below. We know we can't see
548 * anything, so we only need update positions we used to be able
549 * to see.
550 */
551 temp_array = viz_array; /* set viz_array so newsym() will work */
552 viz_array = next_array;
553
554 for (row = 0; row < ROWNO; row++) {
555 old_row = temp_array[row];
556
557 /* Find the min and max positions on the row. */
558 start = min(viz_rmin[row], next_rmin[row]);
559 stop = max(viz_rmax[row], next_rmax[row]);
560
561 for (col = start; col <= stop; col++)
562 if (old_row[col] & IN_SIGHT) newsym(col,row);
563 }
564
565 /* skip the normal update loop */
566 goto skip;
567 }
568 #ifdef REINCARNATION
569 else if (Is_rogue_level(&u.uz)) {
570 rogue_vision(next_array,next_rmin,next_rmax);
571 }
572 #endif
573 else {
574 int has_night_vision = 1; /* hero has night vision */
575
576 if (Underwater && !Is_waterlevel(&u.uz)) {
577 /*
578 * The hero is under water. Only see surrounding locations if
579 * they are also underwater. This overrides night vision but
580 * does not override x-ray vision.
581 */
582 has_night_vision = 0;
583
584 for (row = u.uy-1; row <= u.uy+1; row++)
585 for (col = u.ux-1; col <= u.ux+1; col++) {
586 if (!isok(col,row) || !is_pool(col,row)) continue;
587
588 next_rmin[row] = min(next_rmin[row], col);
589 next_rmax[row] = max(next_rmax[row], col);
590 next_array[row][col] = IN_SIGHT | COULD_SEE;
591 }
592 }
593
594 /* if in a pit, just update for immediate locations */
595 else if (u.utrap && u.utraptype == TT_PIT) {
596 for (row = u.uy-1; row <= u.uy+1; row++) {
597 if (row < 0) continue; if (row >= ROWNO) break;
598
599 next_rmin[row] = max( 0, u.ux - 1);
600 next_rmax[row] = min(COLNO-1, u.ux + 1);
601 next_row = next_array[row];
602
603 for(col=next_rmin[row]; col <= next_rmax[row]; col++)
604 next_row[col] = IN_SIGHT | COULD_SEE;
605 }
606 } else
607 view_from(u.uy, u.ux, next_array, next_rmin, next_rmax,
608 0, (void FDECL((*),(int,int,genericptr_t)))0, (genericptr_t)0);
609
610 /*
611 * Set the IN_SIGHT bit for xray and night vision.
612 */
613 if (u.xray_range >= 0) {
614 if (u.xray_range) {
615 ranges = circle_ptr(u.xray_range);
616
617 for (row = u.uy-u.xray_range; row <= u.uy+u.xray_range; row++) {
618 if (row < 0) continue; if (row >= ROWNO) break;
619 dy = v_abs(u.uy-row); next_row = next_array[row];
620
621 start = max( 0, u.ux - ranges[dy]);
622 stop = min(COLNO-1, u.ux + ranges[dy]);
623
624 for (col = start; col <= stop; col++) {
625 char old_row_val = next_row[col];
626 next_row[col] |= IN_SIGHT;
627 oldseenv = levl[col][row].seenv;
628 levl[col][row].seenv = SVALL; /* see all! */
629 /* Update if previously not in sight or new angle. */
630 if (!(old_row_val & IN_SIGHT) || oldseenv != SVALL)
631 newsym(col,row);
632 }
633
634 next_rmin[row] = min(start, next_rmin[row]);
635 next_rmax[row] = max(stop, next_rmax[row]);
636 }
637
638 } else { /* range is 0 */
639 next_array[u.uy][u.ux] |= IN_SIGHT;
640 levl[u.ux][u.uy].seenv = SVALL;
641 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
642 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
643 }
644 }
645
646 if (has_night_vision && u.xray_range < u.nv_range) {
647 if (!u.nv_range) { /* range is 0 */
648 next_array[u.uy][u.ux] |= IN_SIGHT;
649 levl[u.ux][u.uy].seenv = SVALL;
650 next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]);
651 next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]);
652 } else if (u.nv_range > 0) {
653 ranges = circle_ptr(u.nv_range);
654
655 for (row = u.uy-u.nv_range; row <= u.uy+u.nv_range; row++) {
656 if (row < 0) continue; if (row >= ROWNO) break;
657 dy = v_abs(u.uy-row); next_row = next_array[row];
658
659 start = max( 0, u.ux - ranges[dy]);
660 stop = min(COLNO-1, u.ux + ranges[dy]);
661
662 for (col = start; col <= stop; col++)
663 if (next_row[col]) next_row[col] |= IN_SIGHT;
664
665 next_rmin[row] = min(start, next_rmin[row]);
666 next_rmax[row] = max(stop, next_rmax[row]);
667 }
668 }
669 }
670 }
671
672 /* Set the correct bits for all light sources. */
673 do_light_sources(next_array);
674
675
676 /*
677 * Make the viz_array the new array so that cansee() will work correctly.
678 */
679 temp_array = viz_array;
680 viz_array = next_array;
681
682 /*
683 * The main update loop. Here we do two things:
684 *
685 * + Set the IN_SIGHT bit for places that we could see and are lit.
686 * + Reset changed places.
687 *
688 * There is one thing that make deciding what the hero can see
689 * difficult:
690 *
691 * 1. Directional lighting. Items that block light create problems.
692 * The worst offenders are doors. Suppose a door to a lit room
693 * is closed. It is lit on one side, but not on the other. How
694 * do you know? You have to check the closest adjacent position.
695 * Even so, that is not entirely correct. But it seems close
696 * enough for now.
697 */
698 colbump[u.ux] = colbump[u.ux+1] = 1;
699 for (row = 0; row < ROWNO; row++) {
700 dy = u.uy - row; dy = sign(dy);
701 next_row = next_array[row]; old_row = temp_array[row];
702
703 /* Find the min and max positions on the row. */
704 start = min(viz_rmin[row], next_rmin[row]);
705 stop = max(viz_rmax[row], next_rmax[row]);
706 lev = &levl[start][row];
707
708 sv = &seenv_matrix[dy+1][start < u.ux ? 0 : (start > u.ux ? 2:1)];
709
710 for (col = start; col <= stop;
711 lev += ROWNO, sv += (int) colbump[++col]) {
712 if (next_row[col] & IN_SIGHT) {
713 /*
714 * We see this position because of night- or xray-vision.
715 */
716 oldseenv = lev->seenv;
717 lev->seenv |= new_angle(lev,sv,row,col); /* update seen angle */
718
719 /* Update pos if previously not in sight or new angle. */
720 if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
721 newsym(col,row);
722 }
723
724 else if ((next_row[col] & COULD_SEE)
725 && (lev->lit || (next_row[col] & TEMP_LIT))) {
726 /*
727 * We see this position because it is lit.
728 */
729 if ((IS_DOOR(lev->typ) || lev->typ == SDOOR ||
730 IS_WALL(lev->typ)) && !viz_clear[row][col]) {
731 /*
732 * Make sure doors, walls, boulders or mimics don't show up
733 * at the end of dark hallways. We do this by checking
734 * the adjacent position. If it is lit, then we can see
735 * the door or wall, otherwise we can't.
736 */
737 dx = u.ux - col; dx = sign(dx);
738 flev = &(levl[col+dx][row+dy]);
739 if (flev->lit || next_array[row+dy][col+dx] & TEMP_LIT) {
740 next_row[col] |= IN_SIGHT; /* we see it */
741
742 oldseenv = lev->seenv;
743 lev->seenv |= new_angle(lev,sv,row,col);
744
745 /* Update pos if previously not in sight or new angle.*/
746 if (!(old_row[col] & IN_SIGHT) || oldseenv!=lev->seenv)
747 newsym(col,row);
748 } else
749 goto not_in_sight; /* we don't see it */
750
751 } else {
752 next_row[col] |= IN_SIGHT; /* we see it */
753
754 oldseenv = lev->seenv;
755 lev->seenv |= new_angle(lev,sv,row,col);
756
757 /* Update pos if previously not in sight or new angle. */
758 if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv)
759 newsym(col,row);
760 }
761 } else if ((next_row[col] & COULD_SEE) && lev->waslit) {
762 /*
763 * If we make it here, the hero _could see_ the location,
764 * but doesn't see it (location is not lit).
765 * However, the hero _remembers_ it as lit (waslit is true).
766 * The hero can now see that it is not lit, so change waslit
767 * and update the location.
768 */
769 lev->waslit = 0; /* remember lit condition */
770 newsym(col,row);
771 }
772 /*
773 * At this point we know that the row position is *not* in normal
774 * sight. That is, the position is could be seen, but is dark
775 * or LOS is just plain blocked.
776 *
777 * Update the position if:
778 * o If the old one *was* in sight. We may need to clean up
779 * the glyph -- E.g. darken room spot, etc.
780 * o If we now could see the location (yet the location is not
781 * lit), but previously we couldn't see the location, or vice
782 * versa. Update the spot because there there may be an infared
783 * monster there.
784 */
785 else {
786 not_in_sight:
787 if ((old_row[col] & IN_SIGHT)
788 || ((next_row[col] & COULD_SEE)
789 ^ (old_row[col] & COULD_SEE)))
790 newsym(col,row);
791 }
792
793 } /* end for col . . */
794 } /* end for row . . */
795 colbump[u.ux] = colbump[u.ux+1] = 0;
796
797 skip:
798 /* This newsym() caused a crash delivering msg about failure to open
799 * dungeon file init_dungeons() -> panic() -> done(11) ->
800 * vision_recalc(2) -> newsym() -> crash! u.ux and u.uy are 0 and
801 * program_state.panicking == 1 under those circumstances
802 */
803 if (!program_state.panicking)
804 newsym(u.ux, u.uy); /* Make sure the hero shows up! */
805
806 /* Set the new min and max pointers. */
807 viz_rmin = next_rmin;
808 viz_rmax = next_rmax;
809 }
810
811
812 /*
813 * block_point()
814 *
815 * Make the location opaque to light.
816 */
817 void
block_point(x,y)818 block_point(x,y)
819 int x, y;
820 {
821 fill_point(y,x);
822
823 /* recalc light sources here? */
824
825 /*
826 * We have to do a full vision recalculation if we "could see" the
827 * location. Why? Suppose some monster opened a way so that the
828 * hero could see a lit room. However, the position of the opening
829 * was out of night-vision range of the hero. Suddenly the hero should
830 * see the lit room.
831 */
832 if (viz_array[y][x]) vision_full_recalc = 1;
833 }
834
835 /*
836 * unblock_point()
837 *
838 * Make the location transparent to light.
839 */
840 void
unblock_point(x,y)841 unblock_point(x,y)
842 int x, y;
843 {
844 dig_point(y,x);
845
846 /* recalc light sources here? */
847
848 if (viz_array[y][x]) vision_full_recalc = 1;
849 }
850
851
852 /*===========================================================================*\
853 | |
854 | Everything below this line uses (y,x) instead of (x,y) --- the |
855 | algorithms are faster if they are less recursive and can scan |
856 | on a row longer. |
857 | |
858 \*===========================================================================*/
859
860
861 /* ========================================================================= *\
862 Left and Right Pointer Updates
863 \* ========================================================================= */
864
865 /*
866 * LEFT and RIGHT pointer rules
867 *
868 *
869 * **NOTE** The rules changed on 4/4/90. This comment reflects the
870 * new rules. The change was so that the stone-wall optimization
871 * would work.
872 *
873 * OK, now the tough stuff. We must maintain our left and right
874 * row pointers. The rules are as follows:
875 *
876 * Left Pointers:
877 * ______________
878 *
879 * + If you are a clear spot, your left will point to the first
880 * stone to your left. If there is none, then point the first
881 * legal position in the row (0).
882 *
883 * + If you are a blocked spot, then your left will point to the
884 * left-most blocked spot to your left that is connected to you.
885 * This means that a left-edge (a blocked spot that has an open
886 * spot on its left) will point to itself.
887 *
888 *
889 * Right Pointers:
890 * ---------------
891 * + If you are a clear spot, your right will point to the first
892 * stone to your right. If there is none, then point the last
893 * legal position in the row (COLNO-1).
894 *
895 * + If you are a blocked spot, then your right will point to the
896 * right-most blocked spot to your right that is connected to you.
897 * This means that a right-edge (a blocked spot that has an open
898 * spot on its right) will point to itself.
899 */
900 STATIC_OVL void
dig_point(row,col)901 dig_point(row,col)
902 int row,col;
903 {
904 int i;
905
906 if (viz_clear[row][col]) return; /* already done */
907
908 viz_clear[row][col] = 1;
909
910 /*
911 * Boundary cases first.
912 */
913 if (col == 0) { /* left edge */
914 if (viz_clear[row][1]) {
915 right_ptrs[row][0] = right_ptrs[row][1];
916 } else {
917 right_ptrs[row][0] = 1;
918 for (i = 1; i <= right_ptrs[row][1]; i++)
919 left_ptrs[row][i] = 1;
920 }
921 } else if (col == (COLNO-1)) { /* right edge */
922
923 if (viz_clear[row][COLNO-2]) {
924 left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2];
925 } else {
926 left_ptrs[row][COLNO-1] = COLNO-2;
927 for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++)
928 right_ptrs[row][i] = COLNO-2;
929 }
930 }
931
932 /*
933 * At this point, we know we aren't on the boundaries.
934 */
935 else if (viz_clear[row][col-1] && viz_clear[row][col+1]) {
936 /* Both sides clear */
937 for (i = left_ptrs[row][col-1]; i <= col; i++) {
938 if (!viz_clear[row][i]) continue; /* catch non-end case */
939 right_ptrs[row][i] = right_ptrs[row][col+1];
940 }
941 for (i = col; i <= right_ptrs[row][col+1]; i++) {
942 if (!viz_clear[row][i]) continue; /* catch non-end case */
943 left_ptrs[row][i] = left_ptrs[row][col-1];
944 }
945
946 } else if (viz_clear[row][col-1]) {
947 /* Left side clear, right side blocked. */
948 for (i = col+1; i <= right_ptrs[row][col+1]; i++)
949 left_ptrs[row][i] = col+1;
950
951 for (i = left_ptrs[row][col-1]; i <= col; i++) {
952 if (!viz_clear[row][i]) continue; /* catch non-end case */
953 right_ptrs[row][i] = col+1;
954 }
955 left_ptrs[row][col] = left_ptrs[row][col-1];
956
957 } else if (viz_clear[row][col+1]) {
958 /* Right side clear, left side blocked. */
959 for (i = left_ptrs[row][col-1]; i < col; i++)
960 right_ptrs[row][i] = col-1;
961
962 for (i = col; i <= right_ptrs[row][col+1]; i++) {
963 if (!viz_clear[row][i]) continue; /* catch non-end case */
964 left_ptrs[row][i] = col-1;
965 }
966 right_ptrs[row][col] = right_ptrs[row][col+1];
967
968 } else {
969 /* Both sides blocked */
970 for (i = left_ptrs[row][col-1]; i < col; i++)
971 right_ptrs[row][i] = col-1;
972
973 for (i = col+1; i <= right_ptrs[row][col+1]; i++)
974 left_ptrs[row][i] = col+1;
975
976 left_ptrs[row][col] = col-1;
977 right_ptrs[row][col] = col+1;
978 }
979 }
980
981 STATIC_OVL void
fill_point(row,col)982 fill_point(row,col)
983 int row, col;
984 {
985 int i;
986
987 if (!viz_clear[row][col]) return;
988
989 viz_clear[row][col] = 0;
990
991 if (col == 0) {
992 if (viz_clear[row][1]) { /* adjacent is clear */
993 right_ptrs[row][0] = 0;
994 } else {
995 right_ptrs[row][0] = right_ptrs[row][1];
996 for (i = 1; i <= right_ptrs[row][1]; i++)
997 left_ptrs[row][i] = 0;
998 }
999 } else if (col == COLNO-1) {
1000 if (viz_clear[row][COLNO-2]) { /* adjacent is clear */
1001 left_ptrs[row][COLNO-1] = COLNO-1;
1002 } else {
1003 left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2];
1004 for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++)
1005 right_ptrs[row][i] = COLNO-1;
1006 }
1007 }
1008
1009 /*
1010 * Else we know that we are not on an edge.
1011 */
1012 else if (viz_clear[row][col-1] && viz_clear[row][col+1]) {
1013 /* Both sides clear */
1014 for (i = left_ptrs[row][col-1]+1; i <= col; i++)
1015 right_ptrs[row][i] = col;
1016
1017 if (!left_ptrs[row][col-1]) /* catch the end case */
1018 right_ptrs[row][0] = col;
1019
1020 for (i = col; i < right_ptrs[row][col+1]; i++)
1021 left_ptrs[row][i] = col;
1022
1023 if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */
1024 left_ptrs[row][COLNO-1] = col;
1025
1026 } else if (viz_clear[row][col-1]) {
1027 /* Left side clear, right side blocked. */
1028 for (i = col; i <= right_ptrs[row][col+1]; i++)
1029 left_ptrs[row][i] = col;
1030
1031 for (i = left_ptrs[row][col-1]+1; i < col; i++)
1032 right_ptrs[row][i] = col;
1033
1034 if (!left_ptrs[row][col-1]) /* catch the end case */
1035 right_ptrs[row][i] = col;
1036
1037 right_ptrs[row][col] = right_ptrs[row][col+1];
1038
1039 } else if (viz_clear[row][col+1]) {
1040 /* Right side clear, left side blocked. */
1041 for (i = left_ptrs[row][col-1]; i <= col; i++)
1042 right_ptrs[row][i] = col;
1043
1044 for (i = col+1; i < right_ptrs[row][col+1]; i++)
1045 left_ptrs[row][i] = col;
1046
1047 if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */
1048 left_ptrs[row][i] = col;
1049
1050 left_ptrs[row][col] = left_ptrs[row][col-1];
1051
1052 } else {
1053 /* Both sides blocked */
1054 for (i = left_ptrs[row][col-1]; i <= col; i++)
1055 right_ptrs[row][i] = right_ptrs[row][col+1];
1056
1057 for (i = col; i <= right_ptrs[row][col+1]; i++)
1058 left_ptrs[row][i] = left_ptrs[row][col-1];
1059 }
1060 }
1061
1062
1063 /*===========================================================================*/
1064 /*===========================================================================*/
1065 /* Use either algorithm C or D. See the config.h for more details. =========*/
1066
1067 /*
1068 * Variables local to both Algorithms C and D.
1069 */
1070 static int start_row;
1071 static int start_col;
1072 static int step;
1073 static char **cs_rows;
1074 static char *cs_left;
1075 static char *cs_right;
1076
1077 static void FDECL((*vis_func), (int,int,genericptr_t));
1078 static genericptr_t varg;
1079
1080 /*
1081 * Both Algorithms C and D use the following macros.
1082 *
1083 * good_row(z) - Return TRUE if the argument is a legal row.
1084 * set_cs(rowp,col) - Set the local could see array.
1085 * set_min(z) - Save the min value of the argument and the current
1086 * row minimum.
1087 * set_max(z) - Save the max value of the argument and the current
1088 * row maximum.
1089 *
1090 * The last three macros depend on having local pointers row_min, row_max,
1091 * and rowp being set correctly.
1092 */
1093 #define set_cs(rowp,col) (rowp[col] = COULD_SEE)
1094 #define good_row(z) ((z) >= 0 && (z) < ROWNO)
1095 #define set_min(z) if (*row_min > (z)) *row_min = (z)
1096 #define set_max(z) if (*row_max < (z)) *row_max = (z)
1097 #define is_clear(row,col) viz_clear_rows[row][col]
1098
1099 /*
1100 * clear_path() expanded into 4 macros/functions:
1101 *
1102 * q1_path()
1103 * q2_path()
1104 * q3_path()
1105 * q4_path()
1106 *
1107 * "Draw" a line from the start to the given location. Stop if we hit
1108 * something that blocks light. The start and finish points themselves are
1109 * not checked, just the points between them. These routines do _not_
1110 * expect to be called with the same starting and stopping point.
1111 *
1112 * These routines use the generalized integer Bresenham's algorithm (fast
1113 * line drawing) for all quadrants. The algorithm was taken from _Procedural
1114 * Elements for Computer Graphics_, by David F. Rogers. McGraw-Hill, 1985.
1115 */
1116 #ifdef MACRO_CPATH /* quadrant calls are macros */
1117
1118 /*
1119 * When called, the result is in "result".
1120 * The first two arguments (srow,scol) are one end of the path. The next
1121 * two arguments (row,col) are the destination. The last argument is
1122 * used as a C language label. This means that it must be different
1123 * in each pair of calls.
1124 */
1125
1126 /*
1127 * Quadrant I (step < 0).
1128 */
1129 #define q1_path(srow,scol,y2,x2,label) \
1130 { \
1131 int dx, dy; \
1132 register int k, err, x, y, dxs, dys; \
1133 \
1134 x = (scol); y = (srow); \
1135 dx = (x2) - x; dy = y - (y2); \
1136 \
1137 result = 0; /* default to a blocked path */\
1138 \
1139 dxs = dx << 1; /* save the shifted values */\
1140 dys = dy << 1; \
1141 if (dy > dx) { \
1142 err = dxs - dy; \
1143 \
1144 for (k = dy-1; k; k--) { \
1145 if (err >= 0) { \
1146 x++; \
1147 err -= dys; \
1148 } \
1149 y--; \
1150 err += dxs; \
1151 if (!is_clear(y,x)) goto label;/* blocked */\
1152 } \
1153 } else { \
1154 err = dys - dx; \
1155 \
1156 for (k = dx-1; k; k--) { \
1157 if (err >= 0) { \
1158 y--; \
1159 err -= dxs; \
1160 } \
1161 x++; \
1162 err += dys; \
1163 if (!is_clear(y,x)) goto label;/* blocked */\
1164 } \
1165 } \
1166 \
1167 result = 1; \
1168 }
1169
1170 /*
1171 * Quadrant IV (step > 0).
1172 */
1173 #define q4_path(srow,scol,y2,x2,label) \
1174 { \
1175 int dx, dy; \
1176 register int k, err, x, y, dxs, dys; \
1177 \
1178 x = (scol); y = (srow); \
1179 dx = (x2) - x; dy = (y2) - y; \
1180 \
1181 result = 0; /* default to a blocked path */\
1182 \
1183 dxs = dx << 1; /* save the shifted values */\
1184 dys = dy << 1; \
1185 if (dy > dx) { \
1186 err = dxs - dy; \
1187 \
1188 for (k = dy-1; k; k--) { \
1189 if (err >= 0) { \
1190 x++; \
1191 err -= dys; \
1192 } \
1193 y++; \
1194 err += dxs; \
1195 if (!is_clear(y,x)) goto label;/* blocked */\
1196 } \
1197 \
1198 } else { \
1199 err = dys - dx; \
1200 \
1201 for (k = dx-1; k; k--) { \
1202 if (err >= 0) { \
1203 y++; \
1204 err -= dxs; \
1205 } \
1206 x++; \
1207 err += dys; \
1208 if (!is_clear(y,x)) goto label;/* blocked */\
1209 } \
1210 } \
1211 \
1212 result = 1; \
1213 }
1214
1215 /*
1216 * Quadrant II (step < 0).
1217 */
1218 #define q2_path(srow,scol,y2,x2,label) \
1219 { \
1220 int dx, dy; \
1221 register int k, err, x, y, dxs, dys; \
1222 \
1223 x = (scol); y = (srow); \
1224 dx = x - (x2); dy = y - (y2); \
1225 \
1226 result = 0; /* default to a blocked path */\
1227 \
1228 dxs = dx << 1; /* save the shifted values */\
1229 dys = dy << 1; \
1230 if (dy > dx) { \
1231 err = dxs - dy; \
1232 \
1233 for (k = dy-1; k; k--) { \
1234 if (err >= 0) { \
1235 x--; \
1236 err -= dys; \
1237 } \
1238 y--; \
1239 err += dxs; \
1240 if (!is_clear(y,x)) goto label;/* blocked */\
1241 } \
1242 } else { \
1243 err = dys - dx; \
1244 \
1245 for (k = dx-1; k; k--) { \
1246 if (err >= 0) { \
1247 y--; \
1248 err -= dxs; \
1249 } \
1250 x--; \
1251 err += dys; \
1252 if (!is_clear(y,x)) goto label;/* blocked */\
1253 } \
1254 } \
1255 \
1256 result = 1; \
1257 }
1258
1259 /*
1260 * Quadrant III (step > 0).
1261 */
1262 #define q3_path(srow,scol,y2,x2,label) \
1263 { \
1264 int dx, dy; \
1265 register int k, err, x, y, dxs, dys; \
1266 \
1267 x = (scol); y = (srow); \
1268 dx = x - (x2); dy = (y2) - y; \
1269 \
1270 result = 0; /* default to a blocked path */\
1271 \
1272 dxs = dx << 1; /* save the shifted values */\
1273 dys = dy << 1; \
1274 if (dy > dx) { \
1275 err = dxs - dy; \
1276 \
1277 for (k = dy-1; k; k--) { \
1278 if (err >= 0) { \
1279 x--; \
1280 err -= dys; \
1281 } \
1282 y++; \
1283 err += dxs; \
1284 if (!is_clear(y,x)) goto label;/* blocked */\
1285 } \
1286 \
1287 } else { \
1288 err = dys - dx; \
1289 \
1290 for (k = dx-1; k; k--) { \
1291 if (err >= 0) { \
1292 y++; \
1293 err -= dxs; \
1294 } \
1295 x--; \
1296 err += dys; \
1297 if (!is_clear(y,x)) goto label;/* blocked */\
1298 } \
1299 } \
1300 \
1301 result = 1; \
1302 }
1303
1304 #else /* quadrants are really functions */
1305
1306 STATIC_DCL int FDECL(_q1_path, (int,int,int,int));
1307 STATIC_DCL int FDECL(_q2_path, (int,int,int,int));
1308 STATIC_DCL int FDECL(_q3_path, (int,int,int,int));
1309 STATIC_DCL int FDECL(_q4_path, (int,int,int,int));
1310
1311 #define q1_path(sy,sx,y,x,dummy) result = _q1_path(sy,sx,y,x)
1312 #define q2_path(sy,sx,y,x,dummy) result = _q2_path(sy,sx,y,x)
1313 #define q3_path(sy,sx,y,x,dummy) result = _q3_path(sy,sx,y,x)
1314 #define q4_path(sy,sx,y,x,dummy) result = _q4_path(sy,sx,y,x)
1315
1316 /*
1317 * Quadrant I (step < 0).
1318 */
1319 STATIC_OVL int
_q1_path(srow,scol,y2,x2)1320 _q1_path(srow,scol,y2,x2)
1321 int scol, srow, y2, x2;
1322 {
1323 int dx, dy;
1324 register int k, err, x, y, dxs, dys;
1325
1326 x = scol; y = srow;
1327 dx = x2 - x; dy = y - y2;
1328
1329 dxs = dx << 1; /* save the shifted values */
1330 dys = dy << 1;
1331 if (dy > dx) {
1332 err = dxs - dy;
1333
1334 for (k = dy-1; k; k--) {
1335 if (err >= 0) {
1336 x++;
1337 err -= dys;
1338 }
1339 y--;
1340 err += dxs;
1341 if (!is_clear(y,x)) return 0; /* blocked */
1342 }
1343 } else {
1344 err = dys - dx;
1345
1346 for (k = dx-1; k; k--) {
1347 if (err >= 0) {
1348 y--;
1349 err -= dxs;
1350 }
1351 x++;
1352 err += dys;
1353 if (!is_clear(y,x)) return 0;/* blocked */
1354 }
1355 }
1356
1357 return 1;
1358 }
1359
1360 /*
1361 * Quadrant IV (step > 0).
1362 */
1363 STATIC_OVL int
_q4_path(srow,scol,y2,x2)1364 _q4_path(srow,scol,y2,x2)
1365 int scol, srow, y2, x2;
1366 {
1367 int dx, dy;
1368 register int k, err, x, y, dxs, dys;
1369
1370 x = scol; y = srow;
1371 dx = x2 - x; dy = y2 - y;
1372
1373 dxs = dx << 1; /* save the shifted values */
1374 dys = dy << 1;
1375 if (dy > dx) {
1376 err = dxs - dy;
1377
1378 for (k = dy-1; k; k--) {
1379 if (err >= 0) {
1380 x++;
1381 err -= dys;
1382 }
1383 y++;
1384 err += dxs;
1385 if (!is_clear(y,x)) return 0; /* blocked */
1386 }
1387 } else {
1388 err = dys - dx;
1389
1390 for (k = dx-1; k; k--) {
1391 if (err >= 0) {
1392 y++;
1393 err -= dxs;
1394 }
1395 x++;
1396 err += dys;
1397 if (!is_clear(y,x)) return 0;/* blocked */
1398 }
1399 }
1400
1401 return 1;
1402 }
1403
1404 /*
1405 * Quadrant II (step < 0).
1406 */
1407 STATIC_OVL int
_q2_path(srow,scol,y2,x2)1408 _q2_path(srow,scol,y2,x2)
1409 int scol, srow, y2, x2;
1410 {
1411 int dx, dy;
1412 register int k, err, x, y, dxs, dys;
1413
1414 x = scol; y = srow;
1415 dx = x - x2; dy = y - y2;
1416
1417 dxs = dx << 1; /* save the shifted values */
1418 dys = dy << 1;
1419 if (dy > dx) {
1420 err = dxs - dy;
1421
1422 for (k = dy-1; k; k--) {
1423 if (err >= 0) {
1424 x--;
1425 err -= dys;
1426 }
1427 y--;
1428 err += dxs;
1429 if (!is_clear(y,x)) return 0; /* blocked */
1430 }
1431 } else {
1432 err = dys - dx;
1433
1434 for (k = dx-1; k; k--) {
1435 if (err >= 0) {
1436 y--;
1437 err -= dxs;
1438 }
1439 x--;
1440 err += dys;
1441 if (!is_clear(y,x)) return 0;/* blocked */
1442 }
1443 }
1444
1445 return 1;
1446 }
1447
1448 /*
1449 * Quadrant III (step > 0).
1450 */
1451 STATIC_OVL int
_q3_path(srow,scol,y2,x2)1452 _q3_path(srow,scol,y2,x2)
1453 int scol, srow, y2, x2;
1454 {
1455 int dx, dy;
1456 register int k, err, x, y, dxs, dys;
1457
1458 x = scol; y = srow;
1459 dx = x - x2; dy = y2 - y;
1460
1461 dxs = dx << 1; /* save the shifted values */
1462 dys = dy << 1;
1463 if (dy > dx) {
1464 err = dxs - dy;
1465
1466 for (k = dy-1; k; k--) {
1467 if (err >= 0) {
1468 x--;
1469 err -= dys;
1470 }
1471 y++;
1472 err += dxs;
1473 if (!is_clear(y,x)) return 0; /* blocked */
1474 }
1475 } else {
1476 err = dys - dx;
1477
1478 for (k = dx-1; k; k--) {
1479 if (err >= 0) {
1480 y++;
1481 err -= dxs;
1482 }
1483 x--;
1484 err += dys;
1485 if (!is_clear(y,x)) return 0;/* blocked */
1486 }
1487 }
1488
1489 return 1;
1490 }
1491
1492 #endif /* quadrants are functions */
1493
1494 /*
1495 * Use vision tables to determine if there is a clear path from
1496 * (col1,row1) to (col2,row2). This is used by:
1497 * m_cansee()
1498 * m_canseeu()
1499 * do_light_sources()
1500 */
1501 boolean
clear_path(col1,row1,col2,row2)1502 clear_path(col1,row1,col2,row2)
1503 int col1, row1, col2, row2;
1504 {
1505 int result;
1506
1507 if(col1 < col2) {
1508 if(row1 > row2) {
1509 q1_path(row1,col1,row2,col2,cleardone);
1510 } else {
1511 q4_path(row1,col1,row2,col2,cleardone);
1512 }
1513 } else {
1514 if(row1 > row2) {
1515 q2_path(row1,col1,row2,col2,cleardone);
1516 } else if(row1 == row2 && col1 == col2) {
1517 result = 1;
1518 } else {
1519 q3_path(row1,col1,row2,col2,cleardone);
1520 }
1521 }
1522 #ifdef MACRO_CPATH
1523 cleardone:
1524 #endif
1525 return((boolean)result);
1526 }
1527
1528 #ifdef VISION_TABLES
1529 /*===========================================================================*\
1530 GENERAL LINE OF SIGHT
1531 Algorithm D
1532 \*===========================================================================*/
1533
1534
1535 /*
1536 * Indicate caller for the shadow routines.
1537 */
1538 #define FROM_RIGHT 0
1539 #define FROM_LEFT 1
1540
1541
1542 /*
1543 * Include the table definitions.
1544 */
1545 #include "vis_tab.h"
1546
1547
1548 /* 3D table pointers. */
1549 static close2d *close_dy[CLOSE_MAX_BC_DY];
1550 static far2d *far_dy[FAR_MAX_BC_DY];
1551
1552 STATIC_DCL void FDECL(right_side, (int,int,int,int,int,int,int,char*));
1553 STATIC_DCL void FDECL(left_side, (int,int,int,int,int,int,int,char*));
1554 STATIC_DCL int FDECL(close_shadow, (int,int,int,int));
1555 STATIC_DCL int FDECL(far_shadow, (int,int,int,int));
1556
1557 /*
1558 * Initialize algorithm D's table pointers. If we don't have these,
1559 * then we do 3D table lookups. Verrrry slow.
1560 */
1561 STATIC_OVL void
view_init()1562 view_init()
1563 {
1564 int i;
1565
1566 for (i = 0; i < CLOSE_MAX_BC_DY; i++)
1567 close_dy[i] = &close_table[i];
1568
1569 for (i = 0; i < FAR_MAX_BC_DY; i++)
1570 far_dy[i] = &far_table[i];
1571 }
1572
1573
1574 /*
1575 * If the far table has an entry of OFF_TABLE, then the far block prevents
1576 * us from seeing the location just above/below it. I.e. the first visible
1577 * location is one *before* the block.
1578 */
1579 #define OFF_TABLE 0xff
1580
1581 STATIC_OVL int
close_shadow(side,this_row,block_row,block_col)1582 close_shadow(side,this_row,block_row,block_col)
1583 int side,this_row,block_row,block_col;
1584 {
1585 register int sdy, sdx, pdy, offset;
1586
1587 /*
1588 * If on the same column (block_row = -1), then we can see it.
1589 */
1590 if (block_row < 0) return block_col;
1591
1592 /* Take explicit absolute values. Adjust. */
1593 if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; --sdy; /* src dy */
1594 if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; /* src dx */
1595 if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; /* point dy */
1596
1597 if (sdy < 0 || sdy >= CLOSE_MAX_SB_DY || sdx >= CLOSE_MAX_SB_DX ||
1598 pdy >= CLOSE_MAX_BC_DY) {
1599 impossible("close_shadow: bad value");
1600 return block_col;
1601 }
1602 offset = close_dy[sdy]->close[sdx][pdy];
1603 if (side == FROM_RIGHT)
1604 return block_col + offset;
1605
1606 return block_col - offset;
1607 }
1608
1609
1610 STATIC_OVL int
far_shadow(side,this_row,block_row,block_col)1611 far_shadow(side,this_row,block_row,block_col)
1612 int side,this_row,block_row,block_col;
1613 {
1614 register int sdy, sdx, pdy, offset;
1615
1616 /*
1617 * Take care of a bug that shows up only on the borders.
1618 *
1619 * If the block is beyond the border, then the row is negative. Return
1620 * the block's column number (should be 0 or COLNO-1).
1621 *
1622 * Could easily have the column be -1, but then wouldn't know if it was
1623 * the left or right border.
1624 */
1625 if (block_row < 0) return block_col;
1626
1627 /* Take explicit absolute values. Adjust. */
1628 if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; /* src dy */
1629 if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; --sdx; /* src dx */
1630 if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; --pdy; /* point dy */
1631
1632 if (sdy >= FAR_MAX_SB_DY || sdx < 0 || sdx >= FAR_MAX_SB_DX ||
1633 pdy < 0 || pdy >= FAR_MAX_BC_DY) {
1634 impossible("far_shadow: bad value");
1635 return block_col;
1636 }
1637 if ((offset = far_dy[sdy]->far_q[sdx][pdy]) == OFF_TABLE) offset = -1;
1638 if (side == FROM_RIGHT)
1639 return block_col + offset;
1640
1641 return block_col - offset;
1642 }
1643
1644
1645 /*
1646 * right_side()
1647 *
1648 * Figure out what could be seen on the right side of the source.
1649 */
1650 STATIC_OVL void
right_side(row,cb_row,cb_col,fb_row,fb_col,left,right_mark,limits)1651 right_side(row, cb_row, cb_col, fb_row, fb_col, left, right_mark, limits)
1652 int row; /* current row */
1653 int cb_row, cb_col; /* close block row and col */
1654 int fb_row, fb_col; /* far block row and col */
1655 int left; /* left mark of the previous row */
1656 int right_mark; /* right mark of previous row */
1657 char *limits; /* points at range limit for current row, or NULL */
1658 {
1659 register int i;
1660 register char *rowp;
1661 int hit_stone = 0;
1662 int left_shadow, right_shadow, loc_right;
1663 int lblock_col; /* local block column (current row) */
1664 int nrow, deeper;
1665 char *row_min; /* left most */
1666 char *row_max; /* right most */
1667 int lim_max; /* right most limit of circle */
1668
1669 #ifdef GCC_WARN
1670 rowp = 0;
1671 #endif
1672 nrow = row + step;
1673 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
1674 if(!vis_func) {
1675 rowp = cs_rows[row];
1676 row_min = &cs_left[row];
1677 row_max = &cs_right[row];
1678 }
1679 if(limits) {
1680 lim_max = start_col + *limits;
1681 if(lim_max > COLNO-1) lim_max = COLNO-1;
1682 if(right_mark > lim_max) right_mark = lim_max;
1683 limits++; /* prepare for next row */
1684 } else
1685 lim_max = COLNO-1;
1686
1687 /*
1688 * Get the left shadow from the close block. This value could be
1689 * illegal.
1690 */
1691 left_shadow = close_shadow(FROM_RIGHT,row,cb_row,cb_col);
1692
1693 /*
1694 * Mark all stone walls as seen before the left shadow. All this work
1695 * for a special case.
1696 *
1697 * NOTE. With the addition of this code in here, it is now *required*
1698 * for the algorithm to work correctly. If this is commented out,
1699 * change the above assignment so that left and not left_shadow is the
1700 * variable that gets the shadow.
1701 */
1702 while (left <= right_mark) {
1703 loc_right = right_ptrs[row][left];
1704 if(loc_right > lim_max) loc_right = lim_max;
1705 if (viz_clear_rows[row][left]) {
1706 if (loc_right >= left_shadow) {
1707 left = left_shadow; /* opening ends beyond shadow */
1708 break;
1709 }
1710 left = loc_right;
1711 loc_right = right_ptrs[row][left];
1712 if(loc_right > lim_max) loc_right = lim_max;
1713 if (left == loc_right) return; /* boundary */
1714
1715 /* Shadow covers opening, beyond right mark */
1716 if (left == right_mark && left_shadow > right_mark) return;
1717 }
1718
1719 if (loc_right > right_mark) /* can't see stone beyond the mark */
1720 loc_right = right_mark;
1721
1722 if(vis_func) {
1723 for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg);
1724 } else {
1725 for (i = left; i <= loc_right; i++) set_cs(rowp,i);
1726 set_min(left); set_max(loc_right);
1727 }
1728
1729 if (loc_right == right_mark) return; /* all stone */
1730 if (loc_right >= left_shadow) hit_stone = 1;
1731 left = loc_right + 1;
1732 }
1733
1734 /*
1735 * At this point we are at the first visible clear spot on or beyond
1736 * the left shadow, unless the left shadow is an illegal value. If we
1737 * have "hit stone" then we have a stone wall just to our left.
1738 */
1739
1740 /*
1741 * Get the right shadow. Make sure that it is a legal value.
1742 */
1743 if ((right_shadow = far_shadow(FROM_RIGHT,row,fb_row,fb_col)) >= COLNO)
1744 right_shadow = COLNO-1;
1745 /*
1746 * Make vertical walls work the way we want them. In this case, we
1747 * note when the close block blocks the column just above/beneath
1748 * it (right_shadow < fb_col [actually right_shadow == fb_col-1]). If
1749 * the location is filled, then we want to see it, so we put the
1750 * right shadow back (same as fb_col).
1751 */
1752 if (right_shadow < fb_col && !viz_clear_rows[row][fb_col])
1753 right_shadow = fb_col;
1754 if(right_shadow > lim_max) right_shadow = lim_max;
1755
1756 /*
1757 * Main loop. Within the range of sight of the previous row, mark all
1758 * stone walls as seen. Follow open areas recursively.
1759 */
1760 while (left <= right_mark) {
1761 /* Get the far right of the opening or wall */
1762 loc_right = right_ptrs[row][left];
1763 if(loc_right > lim_max) loc_right = lim_max;
1764
1765 if (!viz_clear_rows[row][left]) {
1766 hit_stone = 1; /* use stone on this row as close block */
1767 /*
1768 * We can see all of the wall until the next open spot or the
1769 * start of the shadow caused by the far block (right).
1770 *
1771 * Can't see stone beyond the right mark.
1772 */
1773 if (loc_right > right_mark) loc_right = right_mark;
1774
1775 if(vis_func) {
1776 for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg);
1777 } else {
1778 for (i = left; i <= loc_right; i++) set_cs(rowp,i);
1779 set_min(left); set_max(loc_right);
1780 }
1781
1782 if (loc_right == right_mark) return; /* hit the end */
1783 left = loc_right + 1;
1784 loc_right = right_ptrs[row][left];
1785 if(loc_right > lim_max) loc_right = lim_max;
1786 /* fall through... we know at least one position is visible */
1787 }
1788
1789 /*
1790 * We are in an opening.
1791 *
1792 * If this is the first open spot since the could see area (this is
1793 * true if we have hit stone), get the shadow generated by the wall
1794 * just to our left.
1795 */
1796 if (hit_stone) {
1797 lblock_col = left-1; /* local block column */
1798 left = close_shadow(FROM_RIGHT,row,row,lblock_col);
1799 if (left > lim_max) break; /* off the end */
1800 }
1801
1802 /*
1803 * Check if the shadow covers the opening. If it does, then
1804 * move to end of the opening. A shadow generated on from a
1805 * wall on this row does *not* cover the wall on the right
1806 * of the opening.
1807 */
1808 if (left >= loc_right) {
1809 if (loc_right == lim_max) { /* boundary */
1810 if (left == lim_max) {
1811 if(vis_func) (*vis_func)(lim_max, row, varg);
1812 else {
1813 set_cs(rowp,lim_max); /* last pos */
1814 set_max(lim_max);
1815 }
1816 }
1817 return; /* done */
1818 }
1819 left = loc_right;
1820 continue;
1821 }
1822
1823 /*
1824 * If the far wall of the opening (loc_right) is closer than the
1825 * shadow limit imposed by the far block (right) then use the far
1826 * wall as our new far block when we recurse.
1827 *
1828 * If the limits are the the same, and the far block really exists
1829 * (fb_row >= 0) then do the same as above.
1830 *
1831 * Normally, the check would be for the far wall being closer OR EQUAL
1832 * to the shadow limit. However, there is a bug that arises from the
1833 * fact that the clear area pointers end in an open space (if it
1834 * exists) on a boundary. This then makes a far block exist where it
1835 * shouldn't --- on a boundary. To get around that, I had to
1836 * introduce the concept of a non-existent far block (when the
1837 * row < 0). Next I have to check for it. Here is where that check
1838 * exists.
1839 */
1840 if ((loc_right < right_shadow) ||
1841 (fb_row >= 0 && loc_right == right_shadow)) {
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);
1847 }
1848
1849 if (deeper) {
1850 if (hit_stone)
1851 right_side(nrow,row,lblock_col,row,loc_right,
1852 left,loc_right,limits);
1853 else
1854 right_side(nrow,cb_row,cb_col,row,loc_right,
1855 left,loc_right,limits);
1856 }
1857
1858 /*
1859 * The following line, setting hit_stone, is needed for those
1860 * walls that are only 1 wide. If hit stone is *not* set and
1861 * the stone is only one wide, then the close block is the old
1862 * one instead one on the current row. A way around having to
1863 * set it here is to make left = loc_right (not loc_right+1) and
1864 * let the outer loop take care of it. However, if we do that
1865 * then we then have to check for boundary conditions here as
1866 * well.
1867 */
1868 hit_stone = 1;
1869
1870 left = loc_right+1;
1871 }
1872 /*
1873 * The opening extends beyond the right mark. This means that
1874 * the next far block is the current far block.
1875 */
1876 else {
1877 if(vis_func) {
1878 for (i=left; i <= right_shadow; i++) (*vis_func)(i, row, varg);
1879 } else {
1880 for (i = left; i <= right_shadow; i++) set_cs(rowp,i);
1881 set_min(left); set_max(right_shadow);
1882 }
1883
1884 if (deeper) {
1885 if (hit_stone)
1886 right_side(nrow, row,lblock_col,fb_row,fb_col,
1887 left,right_shadow,limits);
1888 else
1889 right_side(nrow,cb_row, cb_col,fb_row,fb_col,
1890 left,right_shadow,limits);
1891 }
1892
1893 return; /* we're outta here */
1894 }
1895 }
1896 }
1897
1898
1899 /*
1900 * left_side()
1901 *
1902 * This routine is the mirror image of right_side(). Please see right_side()
1903 * for blow by blow comments.
1904 */
1905 STATIC_OVL void
left_side(row,cb_row,cb_col,fb_row,fb_col,left_mark,right,limits)1906 left_side(row, cb_row, cb_col, fb_row, fb_col, left_mark, right, limits)
1907 int row; /* the current row */
1908 int cb_row, cb_col; /* close block row and col */
1909 int fb_row, fb_col; /* far block row and col */
1910 int left_mark; /* left mark of previous row */
1911 int right; /* right mark of the previous row */
1912 char *limits;
1913 {
1914 register int i;
1915 register char *rowp;
1916 int hit_stone = 0;
1917 int left_shadow, right_shadow, loc_left;
1918 int lblock_col; /* local block column (current row) */
1919 int nrow, deeper;
1920 char *row_min; /* left most */
1921 char *row_max; /* right most */
1922 int lim_min;
1923
1924 #ifdef GCC_WARN
1925 rowp = 0;
1926 #endif
1927 nrow = row + step;
1928 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
1929 if(!vis_func) {
1930 rowp = cs_rows[row];
1931 row_min = &cs_left[row];
1932 row_max = &cs_right[row];
1933 }
1934 if(limits) {
1935 lim_min = start_col - *limits;
1936 if(lim_min < 0) lim_min = 0;
1937 if(left_mark < lim_min) left_mark = lim_min;
1938 limits++; /* prepare for next row */
1939 } else
1940 lim_min = 0;
1941
1942 /* This value could be illegal. */
1943 right_shadow = close_shadow(FROM_LEFT,row,cb_row,cb_col);
1944
1945 while ( right >= left_mark ) {
1946 loc_left = left_ptrs[row][right];
1947 if(loc_left < lim_min) loc_left = lim_min;
1948 if (viz_clear_rows[row][right]) {
1949 if (loc_left <= right_shadow) {
1950 right = right_shadow; /* opening ends beyond shadow */
1951 break;
1952 }
1953 right = loc_left;
1954 loc_left = left_ptrs[row][right];
1955 if(loc_left < lim_min) loc_left = lim_min;
1956 if (right == loc_left) return; /* boundary */
1957 }
1958
1959 if (loc_left < left_mark) /* can't see beyond the left mark */
1960 loc_left = left_mark;
1961
1962 if(vis_func) {
1963 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
1964 } else {
1965 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
1966 set_min(loc_left); set_max(right);
1967 }
1968
1969 if (loc_left == left_mark) return; /* all stone */
1970 if (loc_left <= right_shadow) hit_stone = 1;
1971 right = loc_left - 1;
1972 }
1973
1974 /* At first visible clear spot on or beyond the right shadow. */
1975
1976 if ((left_shadow = far_shadow(FROM_LEFT,row,fb_row,fb_col)) < 0)
1977 left_shadow = 0;
1978
1979 /* Do vertical walls as we want. */
1980 if (left_shadow > fb_col && !viz_clear_rows[row][fb_col])
1981 left_shadow = fb_col;
1982 if(left_shadow < lim_min) left_shadow = lim_min;
1983
1984 while (right >= left_mark) {
1985 loc_left = left_ptrs[row][right];
1986
1987 if (!viz_clear_rows[row][right]) {
1988 hit_stone = 1; /* use stone on this row as close block */
1989
1990 /* We can only see walls until the left mark */
1991 if (loc_left < left_mark) loc_left = left_mark;
1992
1993 if(vis_func) {
1994 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
1995 } else {
1996 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
1997 set_min(loc_left); set_max(right);
1998 }
1999
2000 if (loc_left == left_mark) return; /* hit end */
2001 right = loc_left - 1;
2002 loc_left = left_ptrs[row][right];
2003 if (loc_left < lim_min) loc_left = lim_min;
2004 /* fall through...*/
2005 }
2006
2007 /* We are in an opening. */
2008 if (hit_stone) {
2009 lblock_col = right+1; /* stone block (local) */
2010 right = close_shadow(FROM_LEFT,row,row,lblock_col);
2011 if (right < lim_min) return; /* off the end */
2012 }
2013
2014 /* Check if the shadow covers the opening. */
2015 if (right <= loc_left) {
2016 /* Make a boundary condition work. */
2017 if (loc_left == lim_min) { /* at boundary */
2018 if (right == lim_min) {
2019 if(vis_func) (*vis_func)(lim_min, row, varg);
2020 else {
2021 set_cs(rowp,lim_min); /* caught the last pos */
2022 set_min(lim_min);
2023 }
2024 }
2025 return; /* and break out the loop */
2026 }
2027
2028 right = loc_left;
2029 continue;
2030 }
2031
2032 /* If the far wall of the opening is closer than the shadow limit. */
2033 if ((loc_left > left_shadow) ||
2034 (fb_row >= 0 && loc_left == left_shadow)) {
2035 if(vis_func) {
2036 for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg);
2037 } else {
2038 for (i = loc_left; i <= right; i++) set_cs(rowp,i);
2039 set_min(loc_left); set_max(right);
2040 }
2041
2042 if (deeper) {
2043 if (hit_stone)
2044 left_side(nrow,row,lblock_col,row,loc_left,
2045 loc_left,right,limits);
2046 else
2047 left_side(nrow,cb_row,cb_col,row,loc_left,
2048 loc_left,right,limits);
2049 }
2050
2051 hit_stone = 1; /* needed for walls of width 1 */
2052 right = loc_left-1;
2053 }
2054 /* The opening extends beyond the left mark. */
2055 else {
2056 if(vis_func) {
2057 for (i=left_shadow; i <= right; i++) (*vis_func)(i, row, varg);
2058 } else {
2059 for (i = left_shadow; i <= right; i++) set_cs(rowp,i);
2060 set_min(left_shadow); set_max(right);
2061 }
2062
2063 if (deeper) {
2064 if (hit_stone)
2065 left_side(nrow,row,lblock_col,fb_row,fb_col,
2066 left_shadow,right,limits);
2067 else
2068 left_side(nrow,cb_row,cb_col,fb_row,fb_col,
2069 left_shadow,right,limits);
2070 }
2071
2072 return; /* we're outta here */
2073 }
2074
2075 }
2076 }
2077
2078 /*
2079 * view_from
2080 *
2081 * Calculate a view from the given location. Initialize and fill a
2082 * ROWNOxCOLNO array (could_see) with all the locations that could be
2083 * seen from the source location. Initialize and fill the left most
2084 * and right most boundaries of what could be seen.
2085 */
2086 STATIC_OVL void
view_from(srow,scol,loc_cs_rows,left_most,right_most,range,func,arg)2087 view_from(srow,scol,loc_cs_rows,left_most,right_most, range, func, arg)
2088 int srow, scol; /* source row and column */
2089 char **loc_cs_rows; /* could_see array (row pointers) */
2090 char *left_most, *right_most; /* limits of what could be seen */
2091 int range; /* 0 if unlimited */
2092 void FDECL((*func), (int,int,genericptr_t));
2093 genericptr_t arg;
2094 {
2095 register int i;
2096 char *rowp;
2097 int nrow, left, right, left_row, right_row;
2098 char *limits;
2099
2100 /* Set globals for near_shadow(), far_shadow(), etc. to use. */
2101 start_col = scol;
2102 start_row = srow;
2103 cs_rows = loc_cs_rows;
2104 cs_left = left_most;
2105 cs_right = right_most;
2106 vis_func = func;
2107 varg = arg;
2108
2109 /* Find the left and right limits of sight on the starting row. */
2110 if (viz_clear_rows[srow][scol]) {
2111 left = left_ptrs[srow][scol];
2112 right = right_ptrs[srow][scol];
2113 } else {
2114 left = (!scol) ? 0 :
2115 (viz_clear_rows[srow][scol-1] ? left_ptrs[srow][scol-1] : scol-1);
2116 right = (scol == COLNO-1) ? COLNO-1 :
2117 (viz_clear_rows[srow][scol+1] ? right_ptrs[srow][scol+1] : scol+1);
2118 }
2119
2120 if(range) {
2121 if(range > MAX_RADIUS || range < 1)
2122 panic("view_from called with range %d", range);
2123 limits = circle_ptr(range) + 1; /* start at next row */
2124 if(left < scol - range) left = scol - range;
2125 if(right > scol + range) right = scol + range;
2126 } else
2127 limits = (char*) 0;
2128
2129 if(func) {
2130 for (i = left; i <= right; i++) (*func)(i, srow, arg);
2131 } else {
2132 /* Row optimization */
2133 rowp = cs_rows[srow];
2134
2135 /* We know that we can see our row. */
2136 for (i = left; i <= right; i++) set_cs(rowp,i);
2137 cs_left[srow] = left;
2138 cs_right[srow] = right;
2139 }
2140
2141 /* The far block has a row number of -1 if we are on an edge. */
2142 right_row = (right == COLNO-1) ? -1 : srow;
2143 left_row = (!left) ? -1 : srow;
2144
2145 /*
2146 * Check what could be seen in quadrants.
2147 */
2148 if ( (nrow = srow+1) < ROWNO ) {
2149 step = 1; /* move down */
2150 if (scol<COLNO-1)
2151 right_side(nrow,-1,scol,right_row,right,scol,right,limits);
2152 if (scol)
2153 left_side(nrow,-1,scol,left_row, left, left, scol,limits);
2154 }
2155
2156 if ( (nrow = srow-1) >= 0 ) {
2157 step = -1; /* move up */
2158 if (scol<COLNO-1)
2159 right_side(nrow,-1,scol,right_row,right,scol,right,limits);
2160 if (scol)
2161 left_side(nrow,-1,scol,left_row, left, left, scol,limits);
2162 }
2163 }
2164
2165
2166 #else /*===== End of algorithm D =====*/
2167
2168
2169 /*===========================================================================*\
2170 GENERAL LINE OF SIGHT
2171 Algorithm C
2172 \*===========================================================================*/
2173
2174 /*
2175 * Defines local to Algorithm C.
2176 */
2177 STATIC_DCL void FDECL(right_side, (int,int,int,char*));
2178 STATIC_DCL void FDECL(left_side, (int,int,int,char*));
2179
2180 /* Initialize algorithm C (nothing). */
2181 STATIC_OVL void
view_init()2182 view_init()
2183 {
2184 }
2185
2186 /*
2187 * Mark positions as visible on one quadrant of the right side. The
2188 * quadrant is determined by the value of the global variable step.
2189 */
2190 STATIC_OVL void
right_side(row,left,right_mark,limits)2191 right_side(row, left, right_mark, limits)
2192 int row; /* current row */
2193 int left; /* first (left side) visible spot on prev row */
2194 int right_mark; /* last (right side) visible spot on prev row */
2195 char *limits; /* points at range limit for current row, or NULL */
2196 {
2197 int right; /* right limit of "could see" */
2198 int right_edge; /* right edge of an opening */
2199 int nrow; /* new row (calculate once) */
2200 int deeper; /* if TRUE, call self as needed */
2201 int result; /* set by q?_path() */
2202 register int i; /* loop counter */
2203 register char *rowp; /* row optimization */
2204 char *row_min; /* left most [used by macro set_min()] */
2205 char *row_max; /* right most [used by macro set_max()] */
2206 int lim_max; /* right most limit of circle */
2207
2208 #ifdef GCC_WARN
2209 rowp = row_min = row_max = 0;
2210 #endif
2211 nrow = row + step;
2212 /*
2213 * Can go deeper if the row is in bounds and the next row is within
2214 * the circle's limit. We tell the latter by checking to see if the next
2215 * limit value is the start of a new circle radius (meaning we depend
2216 * on the structure of circle_data[]).
2217 */
2218 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
2219 if(!vis_func) {
2220 rowp = cs_rows[row]; /* optimization */
2221 row_min = &cs_left[row];
2222 row_max = &cs_right[row];
2223 }
2224 if(limits) {
2225 lim_max = start_col + *limits;
2226 if(lim_max > COLNO-1) lim_max = COLNO-1;
2227 if(right_mark > lim_max) right_mark = lim_max;
2228 limits++; /* prepare for next row */
2229 } else
2230 lim_max = COLNO-1;
2231
2232 while (left <= right_mark) {
2233 right_edge = right_ptrs[row][left];
2234 if(right_edge > lim_max) right_edge = lim_max;
2235
2236 if (!is_clear(row,left)) {
2237 /*
2238 * Jump to the far side of a stone wall. We can set all
2239 * the points in between as seen.
2240 *
2241 * If the right edge goes beyond the right mark, check to see
2242 * how much we can see.
2243 */
2244 if (right_edge > right_mark) {
2245 /*
2246 * If the mark on the previous row was a clear position,
2247 * the odds are that we can actually see part of the wall
2248 * beyond the mark on this row. If so, then see one beyond
2249 * the mark. Otherwise don't. This is a kludge so corners
2250 * with an adjacent doorway show up in nethack.
2251 */
2252 right_edge = is_clear(row-step,right_mark) ?
2253 right_mark+1 : right_mark;
2254 }
2255 if(vis_func) {
2256 for (i = left; i <= right_edge; i++) (*vis_func)(i, row, varg);
2257 } else {
2258 for (i = left; i <= right_edge; i++) set_cs(rowp,i);
2259 set_min(left); set_max(right_edge);
2260 }
2261 left = right_edge + 1; /* no limit check necessary */
2262 continue;
2263 }
2264
2265 /* No checking needed if our left side is the start column. */
2266 if (left != start_col) {
2267 /*
2268 * Find the left side. Move right until we can see it or we run
2269 * into a wall.
2270 */
2271 for (; left <= right_edge; left++) {
2272 if (step < 0) {
2273 q1_path(start_row,start_col,row,left,rside1);
2274 } else {
2275 q4_path(start_row,start_col,row,left,rside1);
2276 }
2277 rside1: /* used if q?_path() is a macro */
2278 if (result) break;
2279 }
2280
2281 /*
2282 * Check for boundary conditions. We *need* check (2) to break
2283 * an infinite loop where:
2284 *
2285 * left == right_edge == right_mark == lim_max.
2286 *
2287 */
2288 if (left > lim_max) return; /* check (1) */
2289 if (left == lim_max) { /* check (2) */
2290 if(vis_func) (*vis_func)(lim_max, row, varg);
2291 else {
2292 set_cs(rowp,lim_max);
2293 set_max(lim_max);
2294 }
2295 return;
2296 }
2297 /*
2298 * Check if we can see any spots in the opening. We might
2299 * (left == right_edge) or might not (left == right_edge+1) have
2300 * been able to see the far wall. Make sure we *can* see the
2301 * wall (remember, we can see the spot above/below this one)
2302 * by backing up.
2303 */
2304 if (left >= right_edge) {
2305 left = right_edge; /* for the case left == right_edge+1 */
2306 continue;
2307 }
2308 }
2309
2310 /*
2311 * Find the right side. If the marker from the previous row is
2312 * closer than the edge on this row, then we have to check
2313 * how far we can see around the corner (under the overhang). Stop
2314 * at the first non-visible spot or we actually hit the far wall.
2315 *
2316 * Otherwise, we know we can see the right edge of the current row.
2317 *
2318 * This must be a strict less than so that we can always see a
2319 * horizontal wall, even if it is adjacent to us.
2320 */
2321 if (right_mark < right_edge) {
2322 for (right = right_mark; right <= right_edge; right++) {
2323 if (step < 0) {
2324 q1_path(start_row,start_col,row,right,rside2);
2325 } else {
2326 q4_path(start_row,start_col,row,right,rside2);
2327 }
2328 rside2: /* used if q?_path() is a macro */
2329 if (!result) break;
2330 }
2331 --right; /* get rid of the last increment */
2332 }
2333 else
2334 right = right_edge;
2335
2336 /*
2337 * We have the range that we want. Set the bits. Note that
2338 * there is no else --- we no longer handle splinters.
2339 */
2340 if (left <= right) {
2341 /*
2342 * An ugly special case. If you are adjacent to a vertical wall
2343 * and it has a break in it, then the right mark is set to be
2344 * start_col. We *want* to be able to see adjacent vertical
2345 * walls, so we have to set it back.
2346 */
2347 if (left == right && left == start_col &&
2348 start_col < (COLNO-1) && !is_clear(row,start_col+1))
2349 right = start_col+1;
2350
2351 if(right > lim_max) right = lim_max;
2352 /* set the bits */
2353 if(vis_func)
2354 for (i = left; i <= right; i++) (*vis_func)(i, row, varg);
2355 else {
2356 for (i = left; i <= right; i++) set_cs(rowp,i);
2357 set_min(left); set_max(right);
2358 }
2359
2360 /* recursive call for next finger of light */
2361 if (deeper) right_side(nrow,left,right,limits);
2362 left = right + 1; /* no limit check necessary */
2363 }
2364 }
2365 }
2366
2367
2368 /*
2369 * This routine is the mirror image of right_side(). See right_side() for
2370 * extensive comments.
2371 */
2372 STATIC_OVL void
left_side(row,left_mark,right,limits)2373 left_side(row, left_mark, right, limits)
2374 int row, left_mark, right;
2375 char *limits;
2376 {
2377 int left, left_edge, nrow, deeper, result;
2378 register int i;
2379 register char *rowp;
2380 char *row_min, *row_max;
2381 int lim_min;
2382
2383 #ifdef GCC_WARN
2384 rowp = row_min = row_max = 0;
2385 #endif
2386 nrow = row+step;
2387 deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1)));
2388 if(!vis_func) {
2389 rowp = cs_rows[row];
2390 row_min = &cs_left[row];
2391 row_max = &cs_right[row];
2392 }
2393 if(limits) {
2394 lim_min = start_col - *limits;
2395 if(lim_min < 0) lim_min = 0;
2396 if(left_mark < lim_min) left_mark = lim_min;
2397 limits++; /* prepare for next row */
2398 } else
2399 lim_min = 0;
2400
2401 while (right >= left_mark) {
2402 left_edge = left_ptrs[row][right];
2403 if(left_edge < lim_min) left_edge = lim_min;
2404
2405 if (!is_clear(row,right)) {
2406 /* Jump to the far side of a stone wall. */
2407 if (left_edge < left_mark) {
2408 /* Maybe see more (kludge). */
2409 left_edge = is_clear(row-step,left_mark) ?
2410 left_mark-1 : left_mark;
2411 }
2412 if(vis_func) {
2413 for (i = left_edge; i <= right; i++) (*vis_func)(i, row, varg);
2414 } else {
2415 for (i = left_edge; i <= right; i++) set_cs(rowp,i);
2416 set_min(left_edge); set_max(right);
2417 }
2418 right = left_edge - 1; /* no limit check necessary */
2419 continue;
2420 }
2421
2422 if (right != start_col) {
2423 /* Find the right side. */
2424 for (; right >= left_edge; right--) {
2425 if (step < 0) {
2426 q2_path(start_row,start_col,row,right,lside1);
2427 } else {
2428 q3_path(start_row,start_col,row,right,lside1);
2429 }
2430 lside1: /* used if q?_path() is a macro */
2431 if (result) break;
2432 }
2433
2434 /* Check for boundary conditions. */
2435 if (right < lim_min) return;
2436 if (right == lim_min) {
2437 if(vis_func) (*vis_func)(lim_min, row, varg);
2438 else {
2439 set_cs(rowp,lim_min);
2440 set_min(lim_min);
2441 }
2442 return;
2443 }
2444 /* Check if we can see any spots in the opening. */
2445 if (right <= left_edge) {
2446 right = left_edge;
2447 continue;
2448 }
2449 }
2450
2451 /* Find the left side. */
2452 if (left_mark > left_edge) {
2453 for (left = left_mark; left >= left_edge; --left) {
2454 if (step < 0) {
2455 q2_path(start_row,start_col,row,left,lside2);
2456 } else {
2457 q3_path(start_row,start_col,row,left,lside2);
2458 }
2459 lside2: /* used if q?_path() is a macro */
2460 if (!result) break;
2461 }
2462 left++; /* get rid of the last decrement */
2463 }
2464 else
2465 left = left_edge;
2466
2467 if (left <= right) {
2468 /* An ugly special case. */
2469 if (left == right && right == start_col &&
2470 start_col > 0 && !is_clear(row,start_col-1))
2471 left = start_col-1;
2472
2473 if(left < lim_min) left = lim_min;
2474 if(vis_func)
2475 for (i = left; i <= right; i++) (*vis_func)(i, row, varg);
2476 else {
2477 for (i = left; i <= right; i++) set_cs(rowp,i);
2478 set_min(left); set_max(right);
2479 }
2480
2481 /* Recurse */
2482 if (deeper) left_side(nrow,left,right,limits);
2483 right = left - 1; /* no limit check necessary */
2484 }
2485 }
2486 }
2487
2488
2489 /*
2490 * Calculate all possible visible locations from the given location
2491 * (srow,scol). NOTE this is (y,x)! Mark the visible locations in the
2492 * array provided.
2493 */
2494 STATIC_OVL void
view_from(srow,scol,loc_cs_rows,left_most,right_most,range,func,arg)2495 view_from(srow, scol, loc_cs_rows, left_most, right_most, range, func, arg)
2496 int srow, scol; /* starting row and column */
2497 char **loc_cs_rows; /* pointers to the rows of the could_see array */
2498 char *left_most; /* min mark on each row */
2499 char *right_most; /* max mark on each row */
2500 int range; /* 0 if unlimited */
2501 void FDECL((*func), (int,int,genericptr_t));
2502 genericptr_t arg;
2503 {
2504 register int i; /* loop counter */
2505 char *rowp; /* optimization for setting could_see */
2506 int nrow; /* the next row */
2507 int left; /* the left-most visible column */
2508 int right; /* the right-most visible column */
2509 char *limits; /* range limit for next row */
2510
2511 /* Set globals for q?_path(), left_side(), and right_side() to use. */
2512 start_col = scol;
2513 start_row = srow;
2514 cs_rows = loc_cs_rows; /* 'could see' rows */
2515 cs_left = left_most;
2516 cs_right = right_most;
2517 vis_func = func;
2518 varg = arg;
2519
2520 /*
2521 * Determine extent of sight on the starting row.
2522 */
2523 if (is_clear(srow,scol)) {
2524 left = left_ptrs[srow][scol];
2525 right = right_ptrs[srow][scol];
2526 } else {
2527 /*
2528 * When in stone, you can only see your adjacent squares, unless
2529 * you are on an array boundary or a stone/clear boundary.
2530 */
2531 left = (!scol) ? 0 :
2532 (is_clear(srow,scol-1) ? left_ptrs[srow][scol-1] : scol-1);
2533 right = (scol == COLNO-1) ? COLNO-1 :
2534 (is_clear(srow,scol+1) ? right_ptrs[srow][scol+1] : scol+1);
2535 }
2536
2537 if(range) {
2538 if(range > MAX_RADIUS || range < 1)
2539 panic("view_from called with range %d", range);
2540 limits = circle_ptr(range) + 1; /* start at next row */
2541 if(left < scol - range) left = scol - range;
2542 if(right > scol + range) right = scol + range;
2543 } else
2544 limits = (char*) 0;
2545
2546 if(func) {
2547 for (i = left; i <= right; i++) (*func)(i, srow, arg);
2548 } else {
2549 /* Row pointer optimization. */
2550 rowp = cs_rows[srow];
2551
2552 /* We know that we can see our row. */
2553 for (i = left; i <= right; i++) set_cs(rowp,i);
2554 cs_left[srow] = left;
2555 cs_right[srow] = right;
2556 }
2557
2558 /*
2559 * Check what could be seen in quadrants. We need to check for valid
2560 * rows here, since we don't do it in the routines right_side() and
2561 * left_side() [ugliness to remove extra routine calls].
2562 */
2563 if ( (nrow = srow+1) < ROWNO ) { /* move down */
2564 step = 1;
2565 if (scol < COLNO-1) right_side(nrow, scol, right, limits);
2566 if (scol) left_side (nrow, left, scol, limits);
2567 }
2568
2569 if ( (nrow = srow-1) >= 0 ) { /* move up */
2570 step = -1;
2571 if (scol < COLNO-1) right_side(nrow, scol, right, limits);
2572 if (scol) left_side (nrow, left, scol, limits);
2573 }
2574 }
2575
2576 #endif /*===== End of algorithm C =====*/
2577
2578 /*
2579 * AREA OF EFFECT "ENGINE"
2580 *
2581 * Calculate all possible visible locations as viewed from the given location
2582 * (srow,scol) within the range specified. Perform "func" with (x, y) args and
2583 * additional argument "arg" for each square.
2584 *
2585 * If not centered on the hero, just forward arguments to view_from(); it
2586 * will call "func" when necessary. If the hero is the center, use the
2587 * vision matrix and reduce extra work.
2588 */
2589 void
do_clear_area(scol,srow,range,func,arg)2590 do_clear_area(scol,srow,range,func,arg)
2591 int scol, srow, range;
2592 void FDECL((*func), (int,int,genericptr_t));
2593 genericptr_t arg;
2594 {
2595 /* If not centered on hero, do the hard work of figuring the area */
2596 if (scol != u.ux || srow != u.uy)
2597 view_from(srow, scol, (char **)0, (char *)0, (char *)0,
2598 range, func, arg);
2599 else {
2600 register int x;
2601 int y, min_x, max_x, max_y, offset;
2602 char *limits;
2603
2604 if (range > MAX_RADIUS || range < 1)
2605 panic("do_clear_area: illegal range %d", range);
2606 if(vision_full_recalc)
2607 vision_recalc(0); /* recalc vision if dirty */
2608 limits = circle_ptr(range);
2609 if ((max_y = (srow + range)) >= ROWNO) max_y = ROWNO-1;
2610 if ((y = (srow - range)) < 0) y = 0;
2611 for (; y <= max_y; y++) {
2612 offset = limits[v_abs(y-srow)];
2613 if((min_x = (scol - offset)) < 0) min_x = 0;
2614 if((max_x = (scol + offset)) >= COLNO) max_x = COLNO-1;
2615 for (x = min_x; x <= max_x; x++)
2616 if (couldsee(x, y))
2617 (*func)(x, y, arg);
2618 }
2619 }
2620 }
2621
2622 /*vision.c*/
2623