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