OSDN Git Service

arrange document
[jnethack/source.git] / src / display.c
1 /* NetHack 3.6  display.c       $NHDT-Date: 1556835736 2019/05/02 22:22:16 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.101 $ */
2 /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */
3 /* and Dave Cohrs, 1990.                                          */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 /*
7  *                      THE NEW DISPLAY CODE
8  *
9  * The old display code has been broken up into three parts: vision, display,
10  * and drawing.  Vision decides what locations can and cannot be physically
11  * seen by the hero.  Display decides _what_ is displayed at a given location.
12  * Drawing decides _how_ to draw a monster, fountain, sword, etc.
13  *
14  * The display system uses information from the vision system to decide
15  * what to draw at a given location.  The routines for the vision system
16  * can be found in vision.c and vision.h.  The routines for display can
17  * be found in this file (display.c) and display.h.  The drawing routines
18  * are part of the window port.  See doc/window.doc for the drawing
19  * interface.
20  *
21  * The display system deals with an abstraction called a glyph.  Anything
22  * that could possibly be displayed has a unique glyph identifier.
23  *
24  * What is seen on the screen is a combination of what the hero remembers
25  * and what the hero currently sees.  Objects and dungeon features (walls
26  * doors, etc) are remembered when out of sight.  Monsters and temporary
27  * effects are not remembered.  Each location on the level has an
28  * associated glyph.  This is the hero's _memory_ of what he or she has
29  * seen there before.
30  *
31  * Display rules:
32  *
33  *      If the location is in sight, display in order:
34  *              visible (or sensed) monsters
35  *              visible objects
36  *              known traps
37  *              background
38  *
39  *      If the location is out of sight, display in order:
40  *              sensed monsters (via telepathy or persistent detection)
41  *              warning (partly-sensed monster shown as an abstraction)
42  *              memory
43  *
44  *      "Remembered, unseen monster" is handled like an object rather
45  *      than a monster, and stays displayed whether or not it is in sight.
46  *      It is removed when a visible or sensed or warned-of monster gets
47  *      shown at its location or when searching or fighting reveals that
48  *      no monster is there.
49  *
50  *
51  * Here is a list of the major routines in this file to be used externally:
52  *
53  * newsym
54  *
55  * Possibly update the screen location (x,y).  This is the workhorse routine.
56  * It is always correct --- where correct means following the in-sight/out-
57  * of-sight rules.  **Most of the code should use this routine.**  This
58  * routine updates the map and displays monsters.
59  *
60  *
61  * map_background
62  * map_object
63  * map_trap
64  * map_invisible
65  * unmap_object
66  *
67  * If you absolutely must override the in-sight/out-of-sight rules, there
68  * are two possibilities.  First, you can mess with vision to force the
69  * location in sight then use newsym(), or you can  use the map_* routines.
70  * The first has not been tried [no need] and the second is used in the
71  * detect routines --- detect object, magic mapping, etc.  The map_*
72  * routines *change* what the hero remembers.  All changes made by these
73  * routines will be sticky --- they will survive screen redraws.  Do *not*
74  * use these for things that only temporarily change the screen.  These
75  * routines are also used directly by newsym().  unmap_object is used to
76  * clear a remembered object when/if detection reveals it isn't there.
77  *
78  *
79  * show_glyph
80  *
81  * This is direct (no processing in between) buffered access to the screen.
82  * Temporary screen effects are run through this and its companion,
83  * flush_screen().  There is yet a lower level routine, print_glyph(),
84  * but this is unbuffered and graphic dependent (i.e. it must be surrounded
85  * by graphic set-up and tear-down routines).  Do not use print_glyph().
86  *
87  *
88  * see_monsters
89  * see_objects
90  * see_traps
91  *
92  * These are only used when something affects all of the monsters or
93  * objects or traps.  For objects and traps, the only thing is hallucination.
94  * For monsters, there are hallucination and changing from/to blindness, etc.
95  *
96  *
97  * tmp_at
98  *
99  * This is a useful interface for displaying temporary items on the screen.
100  * Its interface is different than previously, so look at it carefully.
101  *
102  *
103  *
104  * Parts of the rm structure that are used:
105  *
106  *      typ     - What is really there.
107  *      glyph   - What the hero remembers.  This will never be a monster.
108  *                Monsters "float" above this.
109  *      lit     - True if the position is lit.  An optimization for
110  *                lit/unlit rooms.
111  *      waslit  - True if the position was *remembered* as lit.
112  *      seenv   - A vector of bits representing the directions from which the
113  *                hero has seen this position.  The vector's primary use is
114  *                determining how walls are seen.  E.g. a wall sometimes looks
115  *                like stone on one side, but is seen as wall from the other.
116  *                Other uses are for unmapping detected objects and felt
117  *                locations, where we need to know if the hero has ever
118  *                seen the location.
119  *      flags   - Additional information for the typ field.  Different for
120  *                each typ.
121  *      horizontal - Indicates whether the wall or door is horizontal or
122  *                vertical.
123  */
124 #include "hack.h"
125
126 STATIC_DCL void FDECL(display_monster,
127                       (XCHAR_P, XCHAR_P, struct monst *, int, XCHAR_P));
128 STATIC_DCL int FDECL(swallow_to_glyph, (int, int));
129 STATIC_DCL void FDECL(display_warning, (struct monst *));
130
131 STATIC_DCL int FDECL(check_pos, (int, int, int));
132 STATIC_DCL int FDECL(get_bk_glyph, (XCHAR_P, XCHAR_P));
133 STATIC_DCL int FDECL(tether_glyph, (int, int));
134
135 /*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
136 #ifdef WA_VERBOSE
137 STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
138 #endif
139
140 STATIC_DCL int FDECL(set_twall, (int, int, int, int, int, int, int, int));
141 STATIC_DCL int FDECL(set_wall, (int, int, int));
142 STATIC_DCL int FDECL(set_corn, (int, int, int, int, int, int, int, int));
143 STATIC_DCL int FDECL(set_crosswall, (int, int));
144 STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int));
145 STATIC_DCL void FDECL(t_warn, (struct rm *));
146 STATIC_DCL int FDECL(wall_angle, (struct rm *));
147
148 #define remember_topology(x, y) (lastseentyp[x][y] = levl[x][y].typ)
149
150 /*
151  * magic_map_background()
152  *
153  * This function is similar to map_background (see below) except we pay
154  * attention to and correct unexplored, lit ROOM and CORR spots.
155  */
156 void
157 magic_map_background(x, y, show)
158 xchar x, y;
159 int show;
160 {
161     int glyph = back_to_glyph(x, y); /* assumes hero can see x,y */
162     struct rm *lev = &levl[x][y];
163
164     /*
165      * Correct for out of sight lit corridors and rooms that the hero
166      * doesn't remember as lit.
167      */
168     if (!cansee(x, y) && !lev->waslit) {
169         /* Floor spaces are dark if unlit.  Corridors are dark if unlit. */
170         if (lev->typ == ROOM && glyph == cmap_to_glyph(S_room))
171             glyph = cmap_to_glyph((flags.dark_room && iflags.use_color)
172                                       ? (DARKROOMSYM)
173                                       : S_stone);
174         else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
175             glyph = cmap_to_glyph(S_corr);
176     }
177     if (level.flags.hero_memory)
178         lev->glyph = glyph;
179     if (show)
180         show_glyph(x, y, glyph);
181
182     remember_topology(x, y);
183 }
184
185 /*
186  * The routines map_background(), map_object(), and map_trap() could just
187  * as easily be:
188  *
189  *      map_glyph(x,y,glyph,show)
190  *
191  * Which is called with the xx_to_glyph() in the call.  Then I can get
192  * rid of 3 routines that don't do very much anyway.  And then stop
193  * having to create fake objects and traps.  However, I am reluctant to
194  * make this change.
195  */
196 /* FIXME: some of these use xchars for x and y, and some use ints.  Make
197  * this consistent.
198  */
199
200 /*
201  * map_background()
202  *
203  * Make the real background part of our map.  This routine assumes that
204  * the hero can physically see the location.  Update the screen if directed.
205  */
206 void
207 map_background(x, y, show)
208 register xchar x, y;
209 register int show;
210 {
211     register int glyph = back_to_glyph(x, y);
212
213     if (level.flags.hero_memory)
214         levl[x][y].glyph = glyph;
215     if (show)
216         show_glyph(x, y, glyph);
217 }
218
219 /*
220  * map_trap()
221  *
222  * Map the trap and print it out if directed.  This routine assumes that the
223  * hero can physically see the location.
224  */
225 void
226 map_trap(trap, show)
227 register struct trap *trap;
228 register int show;
229 {
230     register int x = trap->tx, y = trap->ty;
231     register int glyph = trap_to_glyph(trap, newsym_rn2);
232
233     if (level.flags.hero_memory)
234         levl[x][y].glyph = glyph;
235     if (show)
236         show_glyph(x, y, glyph);
237 }
238
239 /*
240  * map_object()
241  *
242  * Map the given object.  This routine assumes that the hero can physically
243  * see the location of the object.  Update the screen if directed.
244  */
245 void
246 map_object(obj, show)
247 register struct obj *obj;
248 register int show;
249 {
250     register int x = obj->ox, y = obj->oy;
251     register int glyph = obj_to_glyph(obj, newsym_rn2);
252
253     if (level.flags.hero_memory) {
254         /* MRKR: While hallucinating, statues are seen as random monsters */
255         /*       but remembered as random objects.                        */
256
257         if (Hallucination && obj->otyp == STATUE) {
258             levl[x][y].glyph = random_obj_to_glyph(newsym_rn2);
259         } else {
260             levl[x][y].glyph = glyph;
261         }
262     }
263     if (show)
264         show_glyph(x, y, glyph);
265 }
266
267 /*
268  * map_invisible()
269  *
270  * Make the hero remember that a square contains an invisible monster.
271  * This is a special case in that the square will continue to be displayed
272  * this way even when the hero is close enough to see it.  To get rid of
273  * this and display the square's actual contents, use unmap_object() followed
274  * by newsym() if necessary.
275  */
276 void
277 map_invisible(x, y)
278 register xchar x, y;
279 {
280     if (x != u.ux || y != u.uy) { /* don't display I at hero's location */
281         if (level.flags.hero_memory)
282             levl[x][y].glyph = GLYPH_INVISIBLE;
283         show_glyph(x, y, GLYPH_INVISIBLE);
284     }
285 }
286
287 boolean
288 unmap_invisible(x, y)
289 int x, y;
290 {
291     if (isok(x,y) && glyph_is_invisible(levl[x][y].glyph)) {
292         unmap_object(x, y);
293         newsym(x, y);
294         return TRUE;
295     }
296     return FALSE;
297 }
298
299
300 /*
301  * unmap_object()
302  *
303  * Remove something from the map when the hero realizes it's not there any
304  * more.  Replace it with background or known trap, but not with any other
305  * If this is used for detection, a full screen update is imminent anyway;
306  * if this is used to get rid of an invisible monster notation, we might have
307  * to call newsym().
308  */
309 void
310 unmap_object(x, y)
311 register int x, y;
312 {
313     register struct trap *trap;
314
315     if (!level.flags.hero_memory)
316         return;
317
318     if ((trap = t_at(x, y)) != 0 && trap->tseen && !covers_traps(x, y)) {
319         map_trap(trap, 0);
320     } else if (levl[x][y].seenv) {
321         struct rm *lev = &levl[x][y];
322
323         map_background(x, y, 0);
324
325         /* turn remembered dark room squares dark */
326         if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room)
327             && lev->typ == ROOM)
328             lev->glyph = cmap_to_glyph(S_stone);
329     } else {
330         levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
331     }
332 }
333
334 /*
335  * map_location()
336  *
337  * Make whatever at this location show up.  This is only for non-living
338  * things.  This will not handle feeling invisible objects correctly.
339  *
340  * Internal to display.c, this is a #define for speed.
341  */
342 #define _map_location(x, y, show) \
343     {                                                                       \
344         register struct obj *obj;                                           \
345         register struct trap *trap;                                         \
346                                                                             \
347         if ((obj = vobj_at(x, y)) && !covers_objects(x, y))                 \
348             map_object(obj, show);                                          \
349         else if ((trap = t_at(x, y)) && trap->tseen && !covers_traps(x, y)) \
350             map_trap(trap, show);                                           \
351         else                                                                \
352             map_background(x, y, show);                                     \
353                                                                             \
354         remember_topology(x, y);                                            \
355     }
356
357 void
358 map_location(x, y, show)
359 int x, y, show;
360 {
361     _map_location(x, y, show);
362 }
363
364 #define DETECTED 2
365 #define PHYSICALLY_SEEN 1
366 #define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my)))
367
368 /*
369  * display_monster()
370  *
371  * Note that this is *not* a map_XXXX() function!  Monsters sort of float
372  * above everything.
373  *
374  * Yuck.  Display body parts by recognizing that the display position is
375  * not the same as the monster position.  Currently the only body part is
376  * a worm tail.
377  *
378  */
379 STATIC_OVL void
380 display_monster(x, y, mon, sightflags, worm_tail)
381 register xchar x, y;        /* display position */
382 register struct monst *mon; /* monster to display */
383 int sightflags;             /* 1 if the monster is physically seen;
384                                2 if detected using Detect_monsters */
385 xchar worm_tail;            /* mon is actually a worm tail */
386 {
387     boolean mon_mimic = (M_AP_TYPE(mon) != M_AP_NOTHING);
388     int sensed = (mon_mimic && (Protection_from_shape_changers
389                                 || sensemon(mon)));
390     /*
391      * We must do the mimic check first.  If the mimic is mimicing something,
392      * and the location is in sight, we have to change the hero's memory
393      * so that when the position is out of sight, the hero remembers what
394      * the mimic was mimicing.
395      */
396
397     if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) {
398         switch (M_AP_TYPE(mon)) {
399         default:
400             impossible("display_monster:  bad m_ap_type value [ = %d ]",
401                        (int) mon->m_ap_type);
402             /*FALLTHRU*/
403         case M_AP_NOTHING:
404             show_glyph(x, y, mon_to_glyph(mon, newsym_rn2));
405             break;
406
407         case M_AP_FURNITURE: {
408             /*
409              * This is a poor man's version of map_background().  I can't
410              * use map_background() because we are overriding what is in
411              * the 'typ' field.  Maybe have map_background()'s parameters
412              * be (x,y,glyph) instead of just (x,y).
413              *
414              * mappearance is currently set to an S_ index value in
415              * makemon.c.
416              */
417             int sym = mon->mappearance, glyph = cmap_to_glyph(sym);
418
419             levl[x][y].glyph = glyph;
420             if (!sensed) {
421                 show_glyph(x, y, glyph);
422                 /* override real topology with mimic's fake one */
423                 lastseentyp[x][y] = cmap_to_type(sym);
424             }
425             break;
426         }
427
428         case M_AP_OBJECT: {
429             /* Make a fake object to send to map_object(). */
430             struct obj obj;
431
432             obj = zeroobj;
433             obj.ox = x;
434             obj.oy = y;
435             obj.otyp = mon->mappearance;
436             /* might be mimicing a corpse or statue */
437             obj.corpsenm = has_mcorpsenm(mon) ? MCORPSENM(mon) : PM_TENGU;
438             map_object(&obj, !sensed);
439             break;
440         }
441
442         case M_AP_MONSTER:
443             show_glyph(x, y,
444                        monnum_to_glyph(what_mon((int) mon->mappearance,
445                                                 rn2_on_display_rng)));
446             break;
447         }
448     }
449
450     /* If mimic is unsuccessfully mimicing something, display the monster. */
451     if (!mon_mimic || sensed) {
452         int num;
453
454         /* [ALI] Only use detected glyphs when monster wouldn't be
455          * visible by any other means.
456          *
457          * There are no glyphs for "detected pets" so we have to
458          * decide whether to display such things as detected or as tame.
459          * If both are being highlighted in the same way, it doesn't
460          * matter, but if not, showing them as pets is preferrable.
461          */
462         if (mon->mtame && !Hallucination) {
463             if (worm_tail)
464                 num = petnum_to_glyph(PM_LONG_WORM_TAIL);
465             else
466                 num = pet_to_glyph(mon, rn2_on_display_rng);
467         } else if (sightflags == DETECTED) {
468             if (worm_tail)
469                 num = detected_monnum_to_glyph(
470                              what_mon(PM_LONG_WORM_TAIL, rn2_on_display_rng));
471             else
472                 num = detected_mon_to_glyph(mon, rn2_on_display_rng);
473         } else {
474             if (worm_tail)
475                 num = monnum_to_glyph(
476                              what_mon(PM_LONG_WORM_TAIL, rn2_on_display_rng));
477             else
478                 num = mon_to_glyph(mon, rn2_on_display_rng);
479         }
480         show_glyph(x, y, num);
481     }
482 }
483
484 /*
485  * display_warning()
486  *
487  * This is also *not* a map_XXXX() function!  Monster warnings float
488  * above everything just like monsters do, but only if the monster
489  * is not showing.
490  *
491  * Do not call for worm tails.
492  */
493 STATIC_OVL void
494 display_warning(mon)
495 register struct monst *mon;
496 {
497     int x = mon->mx, y = mon->my;
498     int glyph;
499
500     if (mon_warning(mon)) {
501         int wl = Hallucination ?
502             rn2_on_display_rng(WARNCOUNT - 1) + 1 : warning_of(mon);
503         glyph = warning_to_glyph(wl);
504     } else if (MATCH_WARN_OF_MON(mon)) {
505         glyph = mon_to_glyph(mon, rn2_on_display_rng);
506     } else {
507         impossible("display_warning did not match warning type?");
508         return;
509     }
510     /* warning glyph is drawn on the monster layer; unseen
511        monster glyph is drawn on the object/trap/floor layer;
512        if we see a 'warning' move onto 'remembered, unseen' we
513        need to explicitly remove that in order for it to not
514        reappear when the warned-of monster moves off that spot */
515     if (glyph_is_invisible(levl[x][y].glyph))
516         unmap_object(x, y);
517     show_glyph(x, y, glyph);
518 }
519
520 int
521 warning_of(mon)
522 struct monst *mon;
523 {
524     int wl = 0, tmp = 0;
525
526     if (mon_warning(mon)) {
527         tmp = (int) (mon->m_lev / 4);    /* match display.h */
528         wl = (tmp > WARNCOUNT - 1) ? WARNCOUNT - 1 : tmp;
529     }
530     return wl;
531 }
532
533
534 /*
535  * feel_newsym()
536  *
537  * When hero knows what happened to location, even when blind.
538  */
539 void
540 feel_newsym(x, y)
541 xchar x, y;
542 {
543     if (Blind)
544         feel_location(x, y);
545     else
546         newsym(x, y);
547 }
548
549
550 /*
551  * feel_location()
552  *
553  * Feel the given location.  This assumes that the hero is blind and that
554  * the given position is either the hero's or one of the eight squares
555  * adjacent to the hero (except for a boulder push).
556  * If an invisible monster has gone away, that will be discovered.  If an
557  * invisible monster has appeared, this will _not_ be discovered since
558  * searching only finds one monster per turn so we must check that separately.
559  */
560 void
561 feel_location(x, y)
562 xchar x, y;
563 {
564     struct rm *lev;
565     struct obj *boulder;
566     register struct monst *mon;
567
568     if (!isok(x, y))
569         return;
570     lev = &(levl[x][y]);
571     /* If hero's memory of an invisible monster is accurate, we want to keep
572      * him from detecting the same monster over and over again on each turn.
573      * We must return (so we don't erase the monster).  (We must also, in the
574      * search function, be sure to skip over previously detected 'I's.)
575      */
576     if (glyph_is_invisible(lev->glyph) && m_at(x, y))
577         return;
578
579     /* The hero can't feel non pool locations while under water
580        except for lava and ice. */
581     if (Underwater && !Is_waterlevel(&u.uz)
582         && !is_pool_or_lava(x, y) && !is_ice(x, y))
583         return;
584
585     /* Set the seen vector as if the hero had seen it.
586        It doesn't matter if the hero is levitating or not. */
587     set_seenv(lev, u.ux, u.uy, x, y);
588
589     if (!can_reach_floor(FALSE)) {
590         /*
591          * Levitation Rules.  It is assumed that the hero can feel the state
592          * of the walls around herself and can tell if she is in a corridor,
593          * room, or doorway.  Boulders are felt because they are large enough.
594          * Anything else is unknown because the hero can't reach the ground.
595          * This makes things difficult.
596          *
597          * Check (and display) in order:
598          *
599          *      + Stone, walls, and closed doors.
600          *      + Boulders.  [see a boulder before a doorway]
601          *      + Doors.
602          *      + Room/water positions
603          *      + Everything else (hallways!)
604          */
605         if (IS_ROCK(lev->typ)
606             || (IS_DOOR(lev->typ)
607                 && (lev->doormask & (D_LOCKED | D_CLOSED)))) {
608             map_background(x, y, 1);
609         } else if ((boulder = sobj_at(BOULDER, x, y)) != 0) {
610             map_object(boulder, 1);
611         } else if (IS_DOOR(lev->typ)) {
612             map_background(x, y, 1);
613         } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
614             boolean do_room_glyph;
615
616             /*
617              * An open room or water location.  Normally we wouldn't touch
618              * this, but we have to get rid of remembered boulder symbols.
619              * This will only occur in rare occasions when the hero goes
620              * blind and doesn't find a boulder where expected (something
621              * came along and picked it up).  We know that there is not a
622              * boulder at this location.  Show fountains, pools, etc.
623              * underneath if already seen.  Otherwise, show the appropriate
624              * floor symbol.
625              *
626              * Similarly, if the hero digs a hole in a wall or feels a
627              * location that used to contain an unseen monster.  In these
628              * cases, there's no reason to assume anything was underneath,
629              * so just show the appropriate floor symbol.  If something was
630              * embedded in the wall, the glyph will probably already
631              * reflect that.  Don't change the symbol in this case.
632              *
633              * This isn't quite correct.  If the boulder was on top of some
634              * other objects they should be seen once the boulder is removed.
635              * However, we have no way of knowing that what is there now
636              * was there then.  So we let the hero have a lapse of memory.
637              * We could also just display what is currently on the top of the
638              * object stack (if anything).
639              */
640             do_room_glyph = FALSE;
641             if (lev->glyph == objnum_to_glyph(BOULDER)
642                 || glyph_is_invisible(lev->glyph)) {
643                 if (lev->typ != ROOM && lev->seenv)
644                     map_background(x, y, 1);
645                 else
646                     do_room_glyph = TRUE;
647             } else if (lev->glyph >= cmap_to_glyph(S_stone)
648                        && lev->glyph < cmap_to_glyph(S_darkroom)) {
649                 do_room_glyph = TRUE;
650             }
651             if (do_room_glyph) {
652                 lev->glyph = (flags.dark_room && iflags.use_color
653                               && !Is_rogue_level(&u.uz))
654                                  ? cmap_to_glyph(S_darkroom)
655                                  : (lev->waslit ? cmap_to_glyph(S_room)
656                                                 : cmap_to_glyph(S_stone));
657                 show_glyph(x, y, lev->glyph);
658             }
659         } else {
660             /* We feel it (I think hallways are the only things left). */
661             map_background(x, y, 1);
662             /* Corridors are never felt as lit (unless remembered that way) */
663             /* (lit_corridor only).                                         */
664             if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
665                 && !lev->waslit)
666                 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
667             else if (lev->typ == ROOM && flags.dark_room && iflags.use_color
668                      && lev->glyph == cmap_to_glyph(S_room))
669                 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_darkroom));
670         }
671     } else {
672         _map_location(x, y, 1);
673
674         if (Punished) {
675             /*
676              * A ball or chain is only felt if it is first on the object
677              * location list.  Otherwise, we need to clear the felt bit ---
678              * something has been dropped on the ball/chain.  If the bit is
679              * not cleared, then when the ball/chain is moved it will drop
680              * the wrong glyph.
681              */
682             if (uchain->ox == x && uchain->oy == y) {
683                 if (level.objects[x][y] == uchain)
684                     u.bc_felt |= BC_CHAIN;
685                 else
686                     u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
687             }
688             if (!carried(uball) && uball->ox == x && uball->oy == y) {
689                 if (level.objects[x][y] == uball)
690                     u.bc_felt |= BC_BALL;
691                 else
692                     u.bc_felt &= ~BC_BALL; /* do not feel the ball */
693             }
694         }
695
696         /* Floor spaces are dark if unlit.  Corridors are dark if unlit. */
697         if (lev->typ == ROOM && lev->glyph == cmap_to_glyph(S_room)
698             && (!lev->waslit || (flags.dark_room && iflags.use_color)))
699             show_glyph(x, y, lev->glyph = cmap_to_glyph(
700                                  flags.dark_room ? S_darkroom : S_stone));
701         else if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
702                  && !lev->waslit)
703             show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
704     }
705     /* draw monster on top if we can sense it */
706     if ((x != u.ux || y != u.uy) && (mon = m_at(x, y)) != 0 && sensemon(mon))
707         display_monster(x, y, mon,
708                         (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon))
709                             ? PHYSICALLY_SEEN
710                             : DETECTED,
711                         is_worm_tail(mon));
712 }
713
714 /*
715  * newsym()
716  *
717  * Possibly put a new glyph at the given location.
718  */
719 void
720 newsym(x, y)
721 register int x, y;
722 {
723     register struct monst *mon;
724     register struct rm *lev = &(levl[x][y]);
725     register int see_it;
726     register xchar worm_tail;
727
728     if (in_mklev)
729         return;
730 #ifdef HANGUPHANDLING
731     if (program_state.done_hup)
732         return;
733 #endif
734
735     /* only permit updating the hero when swallowed */
736     if (u.uswallow) {
737         if (x == u.ux && y == u.uy)
738             display_self();
739         return;
740     }
741     if (Underwater && !Is_waterlevel(&u.uz)) {
742         /* when underwater, don't do anything unless <x,y> is an
743            adjacent water or lava or ice position */
744         if (!(is_pool_or_lava(x, y) || is_ice(x, y)) || distu(x, y) > 2)
745             return;
746     }
747
748     /* Can physically see the location. */
749     if (cansee(x, y)) {
750         NhRegion *reg = visible_region_at(x, y);
751         /*
752          * Don't use templit here:  E.g.
753          *
754          *      lev->waslit = !!(lev->lit || templit(x,y));
755          *
756          * Otherwise we have the "light pool" problem, where non-permanently
757          * lit areas just out of sight stay remembered as lit.  They should
758          * re-darken.
759          *
760          * Perhaps ALL areas should revert to their "unlit" look when
761          * out of sight.
762          */
763         lev->waslit = (lev->lit != 0); /* remember lit condition */
764
765         /* normal region shown only on accessible positions, but poison clouds
766          * also shown above lava, pools and moats.
767          */
768         if (reg && (ACCESSIBLE(lev->typ)
769                     || (reg->glyph == cmap_to_glyph(S_poisoncloud)
770                         && is_pool_or_lava(x, y)))) {
771             show_region(reg, x, y);
772             return;
773         }
774
775         if (x == u.ux && y == u.uy) {
776             int see_self = canspotself();
777
778             /* update map information for <u.ux,u.uy> (remembered topology
779                and object/known trap/terrain glyph) but only display it if
780                hero can't see him/herself, then show self if appropriate */
781             _map_location(x, y, !see_self);
782             if (see_self)
783                 display_self();
784         } else {
785             mon = m_at(x, y);
786             worm_tail = is_worm_tail(mon);
787             see_it = mon && (mon_visible(mon)
788                              || (!worm_tail && (tp_sensemon(mon)
789                                                 || MATCH_WARN_OF_MON(mon))));
790             if (mon && (see_it || (!worm_tail && Detect_monsters))) {
791                 if (mon->mtrapped) {
792                     struct trap *trap = t_at(x, y);
793                     int tt = trap ? trap->ttyp : NO_TRAP;
794
795                     /* if monster is in a physical trap, you see trap too */
796                     if (tt == BEAR_TRAP || is_pit(tt) || tt == WEB)
797                         trap->tseen = 1;
798                 }
799                 _map_location(x, y, 0); /* map under the monster */
800                 /* also gets rid of any invisibility glyph */
801                 display_monster(x, y, mon,
802                                 see_it ? PHYSICALLY_SEEN : DETECTED,
803                                 worm_tail);
804             } else if (mon && mon_warning(mon) && !is_worm_tail(mon)) {
805                 display_warning(mon);
806             } else if (glyph_is_invisible(lev->glyph)) {
807                 map_invisible(x, y);
808             } else
809                 _map_location(x, y, 1); /* map the location */\
810         }
811
812     /* Can't see the location. */
813     } else {
814         if (x == u.ux && y == u.uy) {
815             feel_location(u.ux, u.uy); /* forces an update */
816
817             if (canspotself())
818                 display_self();
819         } else if ((mon = m_at(x, y)) != 0
820                    && ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)
821                                   || (see_with_infrared(mon)
822                                       && mon_visible(mon)))) != 0
823                        || Detect_monsters)) {
824             /* Seen or sensed monsters are printed every time.
825                This also gets rid of any invisibility glyph. */
826             display_monster(x, y, mon, see_it ? 0 : DETECTED,
827                             is_worm_tail(mon) ? TRUE : FALSE);
828         } else if (mon && mon_warning(mon) && !is_worm_tail(mon)) {
829             display_warning(mon);
830
831         /*
832          * If the location is remembered as being both dark (waslit is false)
833          * and lit (glyph is a lit room or lit corridor) then it was either:
834          *
835          *      (1) A dark location that the hero could see through night
836          *          vision.
837          *      (2) Darkened while out of the hero's sight.  This can happen
838          *          when cursed scroll of light is read.
839          *
840          * In either case, we have to manually correct the hero's memory to
841          * match waslit.  Deciding when to change waslit is non-trivial.
842          *
843          *  Note:  If flags.lit_corridor is set, then corridors act like room
844          *         squares.  That is, they light up if in night vision range.
845          *         If flags.lit_corridor is not set, then corridors will
846          *         remain dark unless lit by a light spell and may darken
847          *         again, as discussed above.
848          *
849          * These checks and changes must be here and not in back_to_glyph().
850          * They are dependent on the position being out of sight.
851          */
852         } else if (Is_rogue_level(&u.uz)) {
853             if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
854                 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
855             else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM
856                      && !lev->waslit)
857                 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone));
858             else
859                 goto show_mem;
860         } else if (!lev->waslit || (flags.dark_room && iflags.use_color)) {
861             if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
862                 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
863             else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM)
864                 show_glyph(x, y, lev->glyph = cmap_to_glyph(DARKROOMSYM));
865             else
866                 goto show_mem;
867         } else {
868  show_mem:
869             show_glyph(x, y, lev->glyph);
870         }
871     }
872 }
873
874 #undef is_worm_tail
875
876 /*
877  * shieldeff()
878  *
879  * Put magic shield pyrotechnics at the given location.  This *could* be
880  * pulled into a platform dependent routine for fancier graphics if desired.
881  */
882 void
883 shieldeff(x, y)
884 xchar x, y;
885 {
886     register int i;
887
888     if (!flags.sparkle)
889         return;
890     if (cansee(x, y)) { /* Don't see anything if can't see the location */
891         for (i = 0; i < SHIELD_COUNT; i++) {
892             show_glyph(x, y, cmap_to_glyph(shield_static[i]));
893             flush_screen(1); /* make sure the glyph shows up */
894             delay_output();
895         }
896         newsym(x, y); /* restore the old information */
897     }
898 }
899
900 int
901 tether_glyph(x, y)
902 int x, y;
903 {
904     int tdx, tdy;
905     tdx = u.ux - x;
906     tdy = u.uy - y;
907     return zapdir_to_glyph(sgn(tdx),sgn(tdy), 2);
908 }
909
910 /*
911  * tmp_at()
912  *
913  * Temporarily place glyphs on the screen.  Do not call delay_output().  It
914  * is up to the caller to decide if it wants to wait [presently, everyone
915  * but explode() wants to delay].
916  *
917  * Call:
918  *      (DISP_BEAM,    glyph)   open, initialize glyph
919  *      (DISP_FLASH,   glyph)   open, initialize glyph
920  *      (DISP_ALWAYS,  glyph)   open, initialize glyph
921  *      (DISP_CHANGE,  glyph)   change glyph
922  *      (DISP_END,     0)       close & clean up (2nd argument doesn't matter)
923  *      (DISP_FREEMEM, 0)       only used to prevent memory leak during exit)
924  *      (x, y)                  display the glyph at the location
925  *
926  * DISP_BEAM   - Display the given glyph at each location, but do not erase
927  *               any until the close call.
928  * DISP_TETHER - Display a tether glyph at each location, and the tethered
929  *               object at the farthest location, but do not erase any
930  *               until the return trip or close.
931  * DISP_FLASH  - Display the given glyph at each location, but erase the
932  *               previous location's glyph.
933  * DISP_ALWAYS - Like DISP_FLASH, but vision is not taken into account.
934  */
935
936 #define TMP_AT_MAX_GLYPHS (COLNO * 2)
937
938 static struct tmp_glyph {
939     coord saved[TMP_AT_MAX_GLYPHS]; /* previously updated positions */
940     int sidx;                       /* index of next unused slot in saved[] */
941     int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
942     int glyph; /* glyph to use when printing */
943     struct tmp_glyph *prev;
944 } tgfirst;
945
946 void
947 tmp_at(x, y)
948 int x, y;
949 {
950     static struct tmp_glyph *tglyph = (struct tmp_glyph *) 0;
951     struct tmp_glyph *tmp;
952
953     switch (x) {
954     case DISP_BEAM:
955     case DISP_ALL:
956     case DISP_TETHER:
957     case DISP_FLASH:
958     case DISP_ALWAYS:
959         if (!tglyph)
960             tmp = &tgfirst;
961         else /* nested effect; we need dynamic memory */
962             tmp = (struct tmp_glyph *) alloc(sizeof *tmp);
963         tmp->prev = tglyph;
964         tglyph = tmp;
965         tglyph->sidx = 0;
966         tglyph->style = x;
967         tglyph->glyph = y;
968         flush_screen(0); /* flush buffered glyphs */
969         return;
970
971     case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
972         while (tglyph) {
973             tmp = tglyph->prev;
974             if (tglyph != &tgfirst)
975                 free((genericptr_t) tglyph);
976             tglyph = tmp;
977         }
978         return;
979
980     default:
981         break;
982     }
983
984     if (!tglyph)
985         panic("tmp_at: tglyph not initialized");
986
987     switch (x) {
988     case DISP_CHANGE:
989         tglyph->glyph = y;
990         break;
991
992     case DISP_END:
993         if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
994             register int i;
995
996             /* Erase (reset) from source to end */
997             for (i = 0; i < tglyph->sidx; i++)
998                 newsym(tglyph->saved[i].x, tglyph->saved[i].y);
999         } else if (tglyph->style == DISP_TETHER) {
1000             int i;
1001
1002             if (y == BACKTRACK && tglyph->sidx > 1) {
1003                 /* backtrack */
1004                 for (i = tglyph->sidx - 1; i > 0; i--) {
1005                     newsym(tglyph->saved[i].x, tglyph->saved[i].y);
1006                     show_glyph(tglyph->saved[i - 1].x,
1007                                tglyph->saved[i - 1].y, tglyph->glyph);
1008                     flush_screen(0);   /* make sure it shows up */
1009                     delay_output();
1010                 }
1011                 tglyph->sidx = 1;
1012             }
1013             for (i = 0; i < tglyph->sidx; i++)
1014                 newsym(tglyph->saved[i].x, tglyph->saved[i].y);
1015         } else {              /* DISP_FLASH or DISP_ALWAYS */
1016             if (tglyph->sidx) /* been called at least once */
1017                 newsym(tglyph->saved[0].x, tglyph->saved[0].y);
1018         }
1019         /* tglyph->sidx = 0; -- about to be freed, so not necessary */
1020         tmp = tglyph->prev;
1021         if (tglyph != &tgfirst)
1022             free((genericptr_t) tglyph);
1023         tglyph = tmp;
1024         break;
1025
1026     default: /* do it */
1027         if (!isok(x, y))
1028             break;
1029         if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
1030             if (tglyph->style != DISP_ALL && !cansee(x, y))
1031                 break;
1032             if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
1033                 break; /* too many locations */
1034             /* save pos for later erasing */
1035             tglyph->saved[tglyph->sidx].x = x;
1036             tglyph->saved[tglyph->sidx].y = y;
1037             tglyph->sidx += 1;
1038         } else if (tglyph->style == DISP_TETHER) {
1039             if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
1040                 break; /* too many locations */
1041             if (tglyph->sidx) {
1042                 int px, py;
1043
1044                 px = tglyph->saved[tglyph->sidx-1].x;
1045                 py = tglyph->saved[tglyph->sidx-1].y;
1046                 show_glyph(px, py, tether_glyph(px, py));
1047             }
1048             /* save pos for later use or erasure */
1049             tglyph->saved[tglyph->sidx].x = x;
1050             tglyph->saved[tglyph->sidx].y = y;
1051             tglyph->sidx += 1;
1052         } else {                /* DISP_FLASH/ALWAYS */
1053             if (tglyph->sidx) { /* not first call, so reset previous pos */
1054                 newsym(tglyph->saved[0].x, tglyph->saved[0].y);
1055                 tglyph->sidx = 0; /* display is presently up to date */
1056             }
1057             if (!cansee(x, y) && tglyph->style != DISP_ALWAYS)
1058                 break;
1059             tglyph->saved[0].x = x;
1060             tglyph->saved[0].y = y;
1061             tglyph->sidx = 1;
1062         }
1063
1064         show_glyph(x, y, tglyph->glyph); /* show it */
1065         flush_screen(0);                 /* make sure it shows up */
1066         break;
1067     } /* end case */
1068 }
1069
1070 /*
1071  * swallowed()
1072  *
1073  * The hero is swallowed.  Show a special graphics sequence for this.  This
1074  * bypasses all of the display routines and messes with buffered screen
1075  * directly.  This method works because both vision and display check for
1076  * being swallowed.
1077  */
1078 void
1079 swallowed(first)
1080 int first;
1081 {
1082     static xchar lastx, lasty; /* last swallowed position */
1083     int swallower, left_ok, rght_ok;
1084
1085     if (first) {
1086         cls();
1087         bot();
1088     } else {
1089         register int x, y;
1090
1091         /* Clear old location */
1092         for (y = lasty - 1; y <= lasty + 1; y++)
1093             for (x = lastx - 1; x <= lastx + 1; x++)
1094                 if (isok(x, y))
1095                     show_glyph(x, y, cmap_to_glyph(S_stone));
1096     }
1097
1098     swallower = monsndx(u.ustuck->data);
1099     /* assume isok(u.ux,u.uy) */
1100     left_ok = isok(u.ux - 1, u.uy);
1101     rght_ok = isok(u.ux + 1, u.uy);
1102     /*
1103      *  Display the hero surrounded by the monster's stomach.
1104      */
1105     if (isok(u.ux, u.uy - 1)) {
1106         if (left_ok)
1107             show_glyph(u.ux - 1, u.uy - 1,
1108                        swallow_to_glyph(swallower, S_sw_tl));
1109         show_glyph(u.ux, u.uy - 1, swallow_to_glyph(swallower, S_sw_tc));
1110         if (rght_ok)
1111             show_glyph(u.ux + 1, u.uy - 1,
1112                        swallow_to_glyph(swallower, S_sw_tr));
1113     }
1114
1115     if (left_ok)
1116         show_glyph(u.ux - 1, u.uy, swallow_to_glyph(swallower, S_sw_ml));
1117     display_self();
1118     if (rght_ok)
1119         show_glyph(u.ux + 1, u.uy, swallow_to_glyph(swallower, S_sw_mr));
1120
1121     if (isok(u.ux, u.uy + 1)) {
1122         if (left_ok)
1123             show_glyph(u.ux - 1, u.uy + 1,
1124                        swallow_to_glyph(swallower, S_sw_bl));
1125         show_glyph(u.ux, u.uy + 1, swallow_to_glyph(swallower, S_sw_bc));
1126         if (rght_ok)
1127             show_glyph(u.ux + 1, u.uy + 1,
1128                        swallow_to_glyph(swallower, S_sw_br));
1129     }
1130
1131     /* Update the swallowed position. */
1132     lastx = u.ux;
1133     lasty = u.uy;
1134 }
1135
1136 /*
1137  * under_water()
1138  *
1139  * Similar to swallowed() in operation.  Shows hero when underwater
1140  * except when in water level.  Special routines exist for that.
1141  */
1142 void
1143 under_water(mode)
1144 int mode;
1145 {
1146     static xchar lastx, lasty;
1147     static boolean dela;
1148     register int x, y;
1149
1150     /* swallowing has a higher precedence than under water */
1151     if (Is_waterlevel(&u.uz) || u.uswallow)
1152         return;
1153
1154     /* full update */
1155     if (mode == 1 || dela) {
1156         cls();
1157         dela = FALSE;
1158
1159     /* delayed full update */
1160     } else if (mode == 2) {
1161         dela = TRUE;
1162         return;
1163
1164     /* limited update */
1165     } else {
1166         for (y = lasty - 1; y <= lasty + 1; y++)
1167             for (x = lastx - 1; x <= lastx + 1; x++)
1168                 if (isok(x, y))
1169                     show_glyph(x, y, cmap_to_glyph(S_stone));
1170     }
1171
1172     /*
1173      * TODO?  Should this honor Xray radius rather than force radius 1?
1174      */
1175
1176     for (x = u.ux - 1; x <= u.ux + 1; x++)
1177         for (y = u.uy - 1; y <= u.uy + 1; y++)
1178             if (isok(x, y) && (is_pool_or_lava(x, y) || is_ice(x, y))) {
1179                 if (Blind && !(x == u.ux && y == u.uy))
1180                     show_glyph(x, y, cmap_to_glyph(S_stone));
1181                 else
1182                     newsym(x, y);
1183             }
1184     lastx = u.ux;
1185     lasty = u.uy;
1186 }
1187
1188 /*
1189  *      under_ground()
1190  *
1191  *      Very restricted display.  You can only see yourself.
1192  */
1193 void
1194 under_ground(mode)
1195 int mode;
1196 {
1197     static boolean dela;
1198
1199     /* swallowing has a higher precedence than under ground */
1200     if (u.uswallow)
1201         return;
1202
1203     /* full update */
1204     if (mode == 1 || dela) {
1205         cls();
1206         dela = FALSE;
1207
1208     /* delayed full update */
1209     } else if (mode == 2) {
1210         dela = TRUE;
1211         return;
1212
1213     /* limited update */
1214     } else {
1215         newsym(u.ux, u.uy);
1216     }
1217 }
1218
1219 /* ======================================================================== */
1220
1221 /*
1222  * Loop through all of the monsters and update them.  Called when:
1223  *      + going blind & telepathic
1224  *      + regaining sight & telepathic
1225  *      + getting and losing infravision
1226  *      + hallucinating
1227  *      + doing a full screen redraw
1228  *      + see invisible times out or a ring of see invisible is taken off
1229  *      + when a potion of see invisible is quaffed or a ring of see
1230  *        invisible is put on
1231  *      + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1232  *      + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1233  *        sit.c]
1234  */
1235 void
1236 see_monsters()
1237 {
1238     register struct monst *mon;
1239     int new_warn_obj_cnt = 0;
1240
1241     if (defer_see_monsters)
1242         return;
1243
1244     for (mon = fmon; mon; mon = mon->nmon) {
1245         if (DEADMONSTER(mon))
1246             continue;
1247         newsym(mon->mx, mon->my);
1248         if (mon->wormno)
1249             see_wsegs(mon);
1250         if (Warn_of_mon && (context.warntype.obj & mon->data->mflags2) != 0L)
1251             new_warn_obj_cnt++;
1252     }
1253     /*
1254      * Make Sting glow blue or stop glowing if required.
1255      */
1256     if (new_warn_obj_cnt != warn_obj_cnt) {
1257         Sting_effects(new_warn_obj_cnt);
1258         warn_obj_cnt = new_warn_obj_cnt;
1259     }
1260
1261     /* when mounted, hero's location gets caught by monster loop */
1262     if (!u.usteed)
1263         newsym(u.ux, u.uy);
1264 }
1265
1266 /*
1267  * Block/unblock light depending on what a mimic is mimicing and if it's
1268  * invisible or not.  Should be called only when the state of See_invisible
1269  * changes.
1270  */
1271 void
1272 set_mimic_blocking()
1273 {
1274     register struct monst *mon;
1275
1276     for (mon = fmon; mon; mon = mon->nmon) {
1277         if (DEADMONSTER(mon))
1278             continue;
1279         if (mon->minvis && is_lightblocker_mappear(mon)) {
1280             if (See_invisible)
1281                 block_point(mon->mx, mon->my);
1282             else
1283                 unblock_point(mon->mx, mon->my);
1284         }
1285     }
1286 }
1287
1288 /*
1289  * Loop through all of the object *locations* and update them.  Called when
1290  *      + hallucinating.
1291  */
1292 void
1293 see_objects()
1294 {
1295     register struct obj *obj;
1296     for (obj = fobj; obj; obj = obj->nobj)
1297         if (vobj_at(obj->ox, obj->oy) == obj)
1298             newsym(obj->ox, obj->oy);
1299 }
1300
1301 /*
1302  * Update hallucinated traps.
1303  */
1304 void
1305 see_traps()
1306 {
1307     struct trap *trap;
1308     int glyph;
1309
1310     for (trap = ftrap; trap; trap = trap->ntrap) {
1311         glyph = glyph_at(trap->tx, trap->ty);
1312         if (glyph_is_trap(glyph))
1313             newsym(trap->tx, trap->ty);
1314     }
1315 }
1316
1317 /*
1318  * Put the cursor on the hero.  Flush all accumulated glyphs before doing it.
1319  */
1320 void
1321 curs_on_u()
1322 {
1323     flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1324 }
1325
1326 int
1327 doredraw()
1328 {
1329     docrt();
1330     return 0;
1331 }
1332
1333 void
1334 docrt()
1335 {
1336     register int x, y;
1337     register struct rm *lev;
1338
1339     if (!u.ux)
1340         return; /* display isn't ready yet */
1341
1342     if (u.uswallow) {
1343         swallowed(1);
1344         return;
1345     }
1346     if (Underwater && !Is_waterlevel(&u.uz)) {
1347         under_water(1);
1348         return;
1349     }
1350     if (u.uburied) {
1351         under_ground(1);
1352         return;
1353     }
1354
1355     /* shut down vision */
1356     vision_recalc(2);
1357
1358     /*
1359      * This routine assumes that cls() does the following:
1360      *      + fills the physical screen with the symbol for rock
1361      *      + clears the glyph buffer
1362      */
1363     cls();
1364
1365     /* display memory */
1366     for (x = 1; x < COLNO; x++) {
1367         lev = &levl[x][0];
1368         for (y = 0; y < ROWNO; y++, lev++)
1369             if (lev->glyph != cmap_to_glyph(S_stone))
1370                 show_glyph(x, y, lev->glyph);
1371     }
1372
1373     /* see what is to be seen */
1374     vision_recalc(0);
1375
1376     /* overlay with monsters */
1377     see_monsters();
1378
1379     /* perm_invent */
1380     update_inventory();
1381
1382     context.botlx = 1; /* force a redraw of the bottom line */
1383 }
1384
1385 /* for panning beyond a clipped region; resend the current map data to
1386    the interface rather than use docrt()'s regeneration of that data */
1387 void
1388 redraw_map()
1389 {
1390     int x, y, glyph;
1391
1392     /*
1393      * Not sure whether this is actually necessary; save and restore did
1394      * used to get much too involved with each dungeon level as it was
1395      * read and written.
1396      *
1397      * !u.ux: display isn't ready yet; (restoring || !on_level()): was part
1398      * of cliparound() but interface shouldn't access this much internals
1399      */
1400     if (!u.ux || restoring || !on_level(&u.uz0, &u.uz))
1401         return;
1402
1403     /*
1404      * This yields sensible clipping when #terrain+getpos is in
1405      * progress and the screen displays something other than what
1406      * the map would currently be showing.
1407      */
1408     for (y = 0; y < ROWNO; ++y)
1409         for (x = 1; x < COLNO; ++x) {
1410             glyph = glyph_at(x, y); /* not levl[x][y].glyph */
1411             print_glyph(WIN_MAP, x, y, glyph, get_bk_glyph(x, y));
1412         }
1413     flush_screen(1);
1414 }
1415
1416 /* ======================================================================== */
1417 /* Glyph Buffering (3rd screen) =========================================== */
1418
1419 typedef struct {
1420     xchar new; /* perhaps move this bit into the rm structure. */
1421     int glyph;
1422 } gbuf_entry;
1423
1424 static gbuf_entry gbuf[ROWNO][COLNO];
1425 static char gbuf_start[ROWNO];
1426 static char gbuf_stop[ROWNO];
1427
1428 /* FIXME: This is a dirty hack, because newsym() doesn't distinguish
1429  * between object piles and single objects, it doesn't mark the location
1430  * for update. */
1431 void
1432 newsym_force(x, y)
1433 register int x, y;
1434 {
1435     newsym(x,y);
1436     gbuf[y][x].new = 1;
1437     if (gbuf_start[y] > x)
1438         gbuf_start[y] = x;
1439     if (gbuf_stop[y] < x)
1440         gbuf_stop[y] = x;
1441 }
1442
1443 /*
1444  * Store the glyph in the 3rd screen for later flushing.
1445  */
1446 void
1447 show_glyph(x, y, glyph)
1448 int x, y, glyph;
1449 {
1450     /*
1451      * Check for bad positions and glyphs.
1452      */
1453     if (!isok(x, y)) {
1454         const char *text;
1455         int offset;
1456
1457         /* column 0 is invalid, but it's often used as a flag, so ignore it */
1458         if (x == 0)
1459             return;
1460
1461         /*
1462          *  This assumes an ordering of the offsets.  See display.h for
1463          *  the definition.
1464          */
1465
1466         if (glyph >= GLYPH_WARNING_OFF
1467             && glyph < GLYPH_STATUE_OFF) { /* a warning */
1468             text = "warning";
1469             offset = glyph - GLYPH_WARNING_OFF;
1470         } else if (glyph >= GLYPH_SWALLOW_OFF) { /* swallow border */
1471             text = "swallow border";
1472             offset = glyph - GLYPH_SWALLOW_OFF;
1473         } else if (glyph >= GLYPH_ZAP_OFF) { /* zap beam */
1474             text = "zap beam";
1475             offset = glyph - GLYPH_ZAP_OFF;
1476         } else if (glyph >= GLYPH_EXPLODE_OFF) { /* explosion */
1477             text = "explosion";
1478             offset = glyph - GLYPH_EXPLODE_OFF;
1479         } else if (glyph >= GLYPH_CMAP_OFF) { /* cmap */
1480             text = "cmap_index";
1481             offset = glyph - GLYPH_CMAP_OFF;
1482         } else if (glyph >= GLYPH_OBJ_OFF) { /* object */
1483             text = "object";
1484             offset = glyph - GLYPH_OBJ_OFF;
1485         } else if (glyph >= GLYPH_RIDDEN_OFF) { /* ridden mon */
1486             text = "ridden mon";
1487             offset = glyph - GLYPH_RIDDEN_OFF;
1488         } else if (glyph >= GLYPH_BODY_OFF) { /* a corpse */
1489             text = "corpse";
1490             offset = glyph - GLYPH_BODY_OFF;
1491         } else if (glyph >= GLYPH_DETECT_OFF) { /* detected mon */
1492             text = "detected mon";
1493             offset = glyph - GLYPH_DETECT_OFF;
1494         } else if (glyph >= GLYPH_INVIS_OFF) { /* invisible mon */
1495             text = "invisible mon";
1496             offset = glyph - GLYPH_INVIS_OFF;
1497         } else if (glyph >= GLYPH_PET_OFF) { /* a pet */
1498             text = "pet";
1499             offset = glyph - GLYPH_PET_OFF;
1500         } else { /* a monster */
1501             text = "monster";
1502             offset = glyph;
1503         }
1504
1505         impossible("show_glyph:  bad pos %d %d with glyph %d [%s %d].", x, y,
1506                    glyph, text, offset);
1507         return;
1508     }
1509
1510     if (glyph >= MAX_GLYPH) {
1511         impossible("show_glyph:  bad glyph %d [max %d] at (%d,%d).", glyph,
1512                    MAX_GLYPH, x, y);
1513         return;
1514     }
1515
1516     if (gbuf[y][x].glyph != glyph || iflags.use_background_glyph) {
1517         gbuf[y][x].glyph = glyph;
1518         gbuf[y][x].new = 1;
1519         if (gbuf_start[y] > x)
1520             gbuf_start[y] = x;
1521         if (gbuf_stop[y] < x)
1522             gbuf_stop[y] = x;
1523     }
1524 }
1525
1526 /*
1527  * Reset the changed glyph borders so that none of the 3rd screen has
1528  * changed.
1529  */
1530 #define reset_glyph_bbox()             \
1531     {                                  \
1532         int i;                         \
1533                                        \
1534         for (i = 0; i < ROWNO; i++) {  \
1535             gbuf_start[i] = COLNO - 1; \
1536             gbuf_stop[i] = 0;          \
1537         }                              \
1538     }
1539
1540 static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1541 /*
1542  * Turn the 3rd screen into stone.
1543  */
1544 void
1545 clear_glyph_buffer()
1546 {
1547     register int x, y;
1548     register gbuf_entry *gptr;
1549
1550     for (y = 0; y < ROWNO; y++) {
1551         gptr = &gbuf[y][0];
1552         for (x = COLNO; x; x--) {
1553             *gptr++ = nul_gbuf;
1554         }
1555     }
1556     reset_glyph_bbox();
1557 }
1558
1559 /*
1560  * Assumes that the indicated positions are filled with S_stone glyphs.
1561  */
1562 void
1563 row_refresh(start, stop, y)
1564 int start, stop, y;
1565 {
1566     register int x;
1567
1568     for (x = start; x <= stop; x++)
1569         if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
1570             print_glyph(WIN_MAP, x, y, gbuf[y][x].glyph, get_bk_glyph(x, y));
1571 }
1572
1573 void
1574 cls()
1575 {
1576     static boolean in_cls = 0;
1577
1578     if (in_cls)
1579         return;
1580     in_cls = TRUE;
1581     display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1582     context.botlx = 1;                    /* force update of botl window */
1583     clear_nhwindow(WIN_MAP);              /* clear physical screen */
1584
1585     clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1586     in_cls = FALSE;
1587 }
1588
1589 /*
1590  * Synch the third screen with the display.
1591  */
1592 void
1593 flush_screen(cursor_on_u)
1594 int cursor_on_u;
1595 {
1596     /* Prevent infinite loops on errors:
1597      *      flush_screen->print_glyph->impossible->pline->flush_screen
1598      */
1599     static int flushing = 0;
1600     static int delay_flushing = 0;
1601     register int x, y;
1602
1603     if (cursor_on_u == -1)
1604         delay_flushing = !delay_flushing;
1605     if (delay_flushing)
1606         return;
1607     if (flushing)
1608         return; /* if already flushing then return */
1609     flushing = 1;
1610 #ifdef HANGUPHANDLING
1611     if (program_state.done_hup)
1612         return;
1613 #endif
1614
1615     for (y = 0; y < ROWNO; y++) {
1616         register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
1617
1618         for (; x <= gbuf_stop[y]; gptr++, x++)
1619             if (gptr->new) {
1620                 print_glyph(WIN_MAP, x, y, gptr->glyph, get_bk_glyph(x, y));
1621                 gptr->new = 0;
1622             }
1623     }
1624
1625     if (cursor_on_u)
1626         curs(WIN_MAP, u.ux, u.uy); /* move cursor to the hero */
1627     display_nhwindow(WIN_MAP, FALSE);
1628     reset_glyph_bbox();
1629     flushing = 0;
1630     if (context.botl || context.botlx)
1631         bot();
1632     else if (iflags.time_botl)
1633         timebot();
1634 }
1635
1636 /* ======================================================================== */
1637
1638 /*
1639  * back_to_glyph()
1640  *
1641  * Use the information in the rm structure at the given position to create
1642  * a glyph of a background.
1643  *
1644  * I had to add a field in the rm structure (horizontal) so that we knew
1645  * if open doors and secret doors were horizontal or vertical.  Previously,
1646  * the screen symbol had the horizontal/vertical information set at
1647  * level generation time.
1648  *
1649  * I used the 'ladder' field (really doormask) for deciding if stairwells
1650  * were up or down.  I didn't want to check the upstairs and dnstairs
1651  * variables.
1652  */
1653 int
1654 back_to_glyph(x, y)
1655 xchar x, y;
1656 {
1657     int idx;
1658     struct rm *ptr = &(levl[x][y]);
1659
1660     switch (ptr->typ) {
1661     case SCORR:
1662     case STONE:
1663         idx = level.flags.arboreal ? S_tree : S_stone;
1664         break;
1665     case ROOM:
1666         idx = S_room;
1667         break;
1668     case CORR:
1669         idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1670         break;
1671     case HWALL:
1672     case VWALL:
1673     case TLCORNER:
1674     case TRCORNER:
1675     case BLCORNER:
1676     case BRCORNER:
1677     case CROSSWALL:
1678     case TUWALL:
1679     case TDWALL:
1680     case TLWALL:
1681     case TRWALL:
1682     case SDOOR:
1683         idx = ptr->seenv ? wall_angle(ptr) : S_stone;
1684         break;
1685     case DOOR:
1686         if (ptr->doormask) {
1687             if (ptr->doormask & D_BROKEN)
1688                 idx = S_ndoor;
1689             else if (ptr->doormask & D_ISOPEN)
1690                 idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
1691             else /* else is closed */
1692                 idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
1693         } else
1694             idx = S_ndoor;
1695         break;
1696     case IRONBARS:
1697         idx = S_bars;
1698         break;
1699     case TREE:
1700         idx = S_tree;
1701         break;
1702     case POOL:
1703     case MOAT:
1704         idx = S_pool;
1705         break;
1706     case STAIRS:
1707         idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
1708         break;
1709     case LADDER:
1710         idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
1711         break;
1712     case FOUNTAIN:
1713         idx = S_fountain;
1714         break;
1715     case SINK:
1716         idx = S_sink;
1717         break;
1718     case ALTAR:
1719         idx = S_altar;
1720         break;
1721     case GRAVE:
1722         idx = S_grave;
1723         break;
1724     case THRONE:
1725         idx = S_throne;
1726         break;
1727     case LAVAPOOL:
1728         idx = S_lava;
1729         break;
1730     case ICE:
1731         idx = S_ice;
1732         break;
1733     case AIR:
1734         idx = S_air;
1735         break;
1736     case CLOUD:
1737         idx = S_cloud;
1738         break;
1739     case WATER:
1740         idx = S_water;
1741         break;
1742     case DBWALL:
1743         idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
1744         break;
1745     case DRAWBRIDGE_UP:
1746         switch (ptr->drawbridgemask & DB_UNDER) {
1747         case DB_MOAT:
1748             idx = S_pool;
1749             break;
1750         case DB_LAVA:
1751             idx = S_lava;
1752             break;
1753         case DB_ICE:
1754             idx = S_ice;
1755             break;
1756         case DB_FLOOR:
1757             idx = S_room;
1758             break;
1759         default:
1760             impossible("Strange db-under: %d",
1761                        ptr->drawbridgemask & DB_UNDER);
1762             idx = S_room; /* something is better than nothing */
1763             break;
1764         }
1765         break;
1766     case DRAWBRIDGE_DOWN:
1767         idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
1768         break;
1769     default:
1770         impossible("back_to_glyph:  unknown level type [ = %d ]", ptr->typ);
1771         idx = S_room;
1772         break;
1773     }
1774
1775     return cmap_to_glyph(idx);
1776 }
1777
1778 /*
1779  * swallow_to_glyph()
1780  *
1781  * Convert a monster number and a swallow location into the correct glyph.
1782  * If you don't want a patchwork monster while hallucinating, decide on
1783  * a random monster in swallowed() and don't use what_mon() here.
1784  */
1785 STATIC_OVL int
1786 swallow_to_glyph(mnum, loc)
1787 int mnum;
1788 int loc;
1789 {
1790     if (loc < S_sw_tl || S_sw_br < loc) {
1791         impossible("swallow_to_glyph: bad swallow location");
1792         loc = S_sw_br;
1793     }
1794     return ((int) (what_mon(mnum, rn2_on_display_rng) << 3) |
1795             (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
1796 }
1797
1798 /*
1799  * zapdir_to_glyph()
1800  *
1801  * Change the given zap direction and beam type into a glyph.  Each beam
1802  * type has four glyphs, one for each of the symbols below.  The order of
1803  * the zap symbols [0-3] as defined in rm.h are:
1804  *
1805  *      |  S_vbeam      ( 0, 1) or ( 0,-1)
1806  *      -  S_hbeam      ( 1, 0) or (-1, 0)
1807  *      \  S_lslant     ( 1, 1) or (-1,-1)
1808  *      /  S_rslant     (-1, 1) or ( 1,-1)
1809  */
1810 int
1811 zapdir_to_glyph(dx, dy, beam_type)
1812 register int dx, dy;
1813 int beam_type;
1814 {
1815     if (beam_type >= NUM_ZAP) {
1816         impossible("zapdir_to_glyph:  illegal beam type");
1817         beam_type = 0;
1818     }
1819     dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
1820
1821     return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
1822 }
1823
1824 /*
1825  * Utility routine for dowhatis() used to find out the glyph displayed at
1826  * the location.  This isn't necessarily the same as the glyph in the levl
1827  * structure, so we must check the "third screen".
1828  */
1829 int
1830 glyph_at(x, y)
1831 xchar x, y;
1832 {
1833     if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
1834         return cmap_to_glyph(S_room); /* XXX */
1835     return gbuf[y][x].glyph;
1836 }
1837
1838 /*
1839  * This will be used to get the glyph for the background so that
1840  * it can potentially be merged into graphical window ports to
1841  * improve the appearance of stuff on dark room squares and the
1842  * plane of air etc.
1843  *
1844  * Until that is working correctly in the branch, however, for now
1845  * we just return NO_GLYPH as an indicator to ignore it.
1846  *
1847  * [This should be using background as recorded for #overview rather
1848  * than current data from the map.]
1849  */
1850
1851 STATIC_OVL int
1852 get_bk_glyph(x, y)
1853 xchar x, y;
1854 {
1855     int idx, bkglyph = NO_GLYPH;
1856     struct rm *lev = &levl[x][y];
1857
1858     if (iflags.use_background_glyph && lev->seenv != 0
1859         && gbuf[y][x].glyph != cmap_to_glyph(S_stone)) {
1860         switch (lev->typ) {
1861         case SCORR:
1862         case STONE:
1863             idx = level.flags.arboreal ? S_tree : S_stone;
1864             break;
1865         case ROOM:
1866            idx = S_room;
1867            break;
1868         case CORR:
1869            idx = (lev->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1870            break;
1871         case ICE:
1872            idx = S_ice;
1873            break;
1874         case AIR:
1875            idx = S_air;
1876            break;
1877         case CLOUD:
1878            idx = S_cloud;
1879            break;
1880         case POOL:
1881         case MOAT:
1882            idx = S_pool;
1883            break;
1884         case WATER:
1885            idx = S_water;
1886            break;
1887         case LAVAPOOL:
1888            idx = S_lava;
1889            break;
1890         default:
1891            idx = S_room;
1892            break;
1893         }
1894
1895         if (!cansee(x, y) && (!lev->waslit || flags.dark_room)) {
1896             /* Floor spaces are dark if unlit.  Corridors are dark if unlit. */
1897             if (lev->typ == CORR && idx == S_litcorr)
1898                 idx = S_corr;
1899             else if (idx == S_room)
1900                 idx = (flags.dark_room && iflags.use_color)
1901                          ? DARKROOMSYM : S_stone;
1902         }
1903
1904         if (idx != S_room)
1905             bkglyph = cmap_to_glyph(idx);
1906     }
1907     return bkglyph;
1908 }
1909
1910 /* ------------------------------------------------------------------------ */
1911 /* Wall Angle ------------------------------------------------------------- */
1912
1913 #ifdef WA_VERBOSE
1914
1915 static const char *FDECL(type_to_name, (int));
1916 static void FDECL(error4, (int, int, int, int, int, int));
1917
1918 static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1919 static const char *type_names[MAX_TYPE] = {
1920     "STONE", "VWALL", "HWALL", "TLCORNER", "TRCORNER", "BLCORNER", "BRCORNER",
1921     "CROSSWALL", "TUWALL", "TDWALL", "TLWALL", "TRWALL", "DBWALL", "TREE",
1922     "SDOOR", "SCORR", "POOL", "MOAT", "WATER", "DRAWBRIDGE_UP", "LAVAPOOL",
1923     "IRON_BARS", "DOOR", "CORR", "ROOM", "STAIRS", "LADDER", "FOUNTAIN",
1924     "THRONE", "SINK", "GRAVE", "ALTAR", "ICE", "DRAWBRIDGE_DOWN", "AIR",
1925     "CLOUD"
1926 };
1927
1928 static const char *
1929 type_to_name(type)
1930 int type;
1931 {
1932     return (type < 0 || type >= MAX_TYPE) ? "unknown" : type_names[type];
1933 }
1934
1935 static void
1936 error4(x, y, a, b, c, dd)
1937 int x, y, a, b, c, dd;
1938 {
1939     pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1940           type_to_name(levl[x][y].typ), x, y,
1941           a ? "1" : "", b ? "2" : "", c ? "3" : "", dd ? "4" : "");
1942     bad_count[levl[x][y].typ]++;
1943 }
1944 #endif /* WA_VERBOSE */
1945
1946 /*
1947  * Return 'which' if position is implies an unfinished exterior.  Return
1948  * zero otherwise.  Unfinished implies outer area is rock or a corridor.
1949  *
1950  * Things that are ambiguous: lava
1951  */
1952 STATIC_OVL int
1953 check_pos(x, y, which)
1954 int x, y, which;
1955 {
1956     int type;
1957
1958     if (!isok(x, y))
1959         return which;
1960     type = levl[x][y].typ;
1961     if (IS_ROCK(type) || type == CORR || type == SCORR)
1962         return which;
1963     return 0;
1964 }
1965
1966 /* Return TRUE if more than one is non-zero. */
1967 /*ARGSUSED*/
1968 #ifdef WA_VERBOSE
1969 STATIC_OVL boolean
1970 more_than_one(x, y, a, b, c)
1971 int x, y, a, b, c;
1972 {
1973     if ((a && (b | c)) || (b && (a | c)) || (c && (a | b))) {
1974         error4(x, y, a, b, c, 0);
1975         return TRUE;
1976     }
1977     return FALSE;
1978 }
1979 #else
1980 #define more_than_one(x, y, a, b, c) \
1981     (((a) && ((b) | (c))) || ((b) && ((a) | (c))) || ((c) && ((a) | (b))))
1982 #endif
1983
1984 /* Return the wall mode for a T wall. */
1985 STATIC_OVL int
1986 set_twall(x0, y0, x1, y1, x2, y2, x3, y3)
1987 int x0, y0; /* used #if WA_VERBOSE */
1988 int x1, y1, x2, y2, x3, y3;
1989 {
1990     int wmode, is_1, is_2, is_3;
1991
1992 #ifndef WA_VERBOSE
1993     /* non-verbose more_than_one() doesn't use these */
1994     nhUse(x0);
1995     nhUse(y0);
1996 #endif
1997
1998     is_1 = check_pos(x1, y1, WM_T_LONG);
1999     is_2 = check_pos(x2, y2, WM_T_BL);
2000     is_3 = check_pos(x3, y3, WM_T_BR);
2001     if (more_than_one(x0, y0, is_1, is_2, is_3)) {
2002         wmode = 0;
2003     } else {
2004         wmode = is_1 + is_2 + is_3;
2005     }
2006     return wmode;
2007 }
2008
2009 /* Return wall mode for a horizontal or vertical wall. */
2010 STATIC_OVL int
2011 set_wall(x, y, horiz)
2012 int x, y, horiz;
2013 {
2014     int wmode, is_1, is_2;
2015
2016     if (horiz) {
2017         is_1 = check_pos(x, y - 1, WM_W_TOP);
2018         is_2 = check_pos(x, y + 1, WM_W_BOTTOM);
2019     } else {
2020         is_1 = check_pos(x - 1, y, WM_W_LEFT);
2021         is_2 = check_pos(x + 1, y, WM_W_RIGHT);
2022     }
2023     if (more_than_one(x, y, is_1, is_2, 0)) {
2024         wmode = 0;
2025     } else {
2026         wmode = is_1 + is_2;
2027     }
2028     return wmode;
2029 }
2030
2031 /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
2032 STATIC_OVL int
2033 set_corn(x1, y1, x2, y2, x3, y3, x4, y4)
2034 int x1, y1, x2, y2, x3, y3, x4, y4;
2035 {
2036     int wmode, is_1, is_2, is_3, is_4;
2037
2038     is_1 = check_pos(x1, y1, 1);
2039     is_2 = check_pos(x2, y2, 1);
2040     is_3 = check_pos(x3, y3, 1);
2041     is_4 = check_pos(x4, y4, 1); /* inner location */
2042
2043     /*
2044      * All 4 should not be true.  So if the inner location is rock,
2045      * use it.  If all of the outer 3 are true, use outer.  We currently
2046      * can't cover the case where only part of the outer is rock, so
2047      * we just say that all the walls are finished (if not overridden
2048      * by the inner section).
2049      */
2050     if (is_4) {
2051         wmode = WM_C_INNER;
2052     } else if (is_1 && is_2 && is_3)
2053         wmode = WM_C_OUTER;
2054     else
2055         wmode = 0; /* finished walls on all sides */
2056
2057     return wmode;
2058 }
2059
2060 /* Return mode for a crosswall. */
2061 STATIC_OVL int
2062 set_crosswall(x, y)
2063 int x, y;
2064 {
2065     int wmode, is_1, is_2, is_3, is_4;
2066
2067     is_1 = check_pos(x - 1, y - 1, 1);
2068     is_2 = check_pos(x + 1, y - 1, 1);
2069     is_3 = check_pos(x + 1, y + 1, 1);
2070     is_4 = check_pos(x - 1, y + 1, 1);
2071
2072     wmode = is_1 + is_2 + is_3 + is_4;
2073     if (wmode > 1) {
2074         if (is_1 && is_3 && (is_2 + is_4 == 0)) {
2075             wmode = WM_X_TLBR;
2076         } else if (is_2 && is_4 && (is_1 + is_3 == 0)) {
2077             wmode = WM_X_BLTR;
2078         } else {
2079 #ifdef WA_VERBOSE
2080             error4(x, y, is_1, is_2, is_3, is_4);
2081 #endif
2082             wmode = 0;
2083         }
2084     } else if (is_1)
2085         wmode = WM_X_TL;
2086     else if (is_2)
2087         wmode = WM_X_TR;
2088     else if (is_3)
2089         wmode = WM_X_BR;
2090     else if (is_4)
2091         wmode = WM_X_BL;
2092
2093     return wmode;
2094 }
2095
2096 /* Called from mklev.  Scan the level and set the wall modes. */
2097 void
2098 set_wall_state()
2099 {
2100     int x, y;
2101     int wmode;
2102     struct rm *lev;
2103
2104 #ifdef WA_VERBOSE
2105     for (x = 0; x < MAX_TYPE; x++)
2106         bad_count[x] = 0;
2107 #endif
2108
2109     for (x = 0; x < COLNO; x++)
2110         for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
2111             switch (lev->typ) {
2112             case SDOOR:
2113                 wmode = set_wall(x, y, (int) lev->horizontal);
2114                 break;
2115             case VWALL:
2116                 wmode = set_wall(x, y, 0);
2117                 break;
2118             case HWALL:
2119                 wmode = set_wall(x, y, 1);
2120                 break;
2121             case TDWALL:
2122                 wmode = set_twall(x, y, x, y - 1, x - 1, y + 1, x + 1, y + 1);
2123                 break;
2124             case TUWALL:
2125                 wmode = set_twall(x, y, x, y + 1, x + 1, y - 1, x - 1, y - 1);
2126                 break;
2127             case TLWALL:
2128                 wmode = set_twall(x, y, x + 1, y, x - 1, y - 1, x - 1, y + 1);
2129                 break;
2130             case TRWALL:
2131                 wmode = set_twall(x, y, x - 1, y, x + 1, y + 1, x + 1, y - 1);
2132                 break;
2133             case TLCORNER:
2134                 wmode =
2135                     set_corn(x - 1, y - 1, x, y - 1, x - 1, y, x + 1, y + 1);
2136                 break;
2137             case TRCORNER:
2138                 wmode =
2139                     set_corn(x, y - 1, x + 1, y - 1, x + 1, y, x - 1, y + 1);
2140                 break;
2141             case BLCORNER:
2142                 wmode =
2143                     set_corn(x, y + 1, x - 1, y + 1, x - 1, y, x + 1, y - 1);
2144                 break;
2145             case BRCORNER:
2146                 wmode =
2147                     set_corn(x + 1, y, x + 1, y + 1, x, y + 1, x - 1, y - 1);
2148                 break;
2149             case CROSSWALL:
2150                 wmode = set_crosswall(x, y);
2151                 break;
2152
2153             default:
2154                 wmode = -1; /* don't set wall info */
2155                 break;
2156             }
2157
2158             if (wmode >= 0)
2159                 lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
2160         }
2161
2162 #ifdef WA_VERBOSE
2163     /* check if any bad positions found */
2164     for (x = y = 0; x < MAX_TYPE; x++)
2165         if (bad_count[x]) {
2166             if (y == 0) {
2167                 y = 1; /* only print once */
2168                 pline("set_wall_type: wall mode problems with: ");
2169             }
2170             pline("%s %d;", type_names[x], bad_count[x]);
2171         }
2172 #endif /* WA_VERBOSE */
2173 }
2174
2175 /* ------------------------------------------------------------------------ */
2176 /* This matrix is used here and in vision.c. */
2177 unsigned char seenv_matrix[3][3] = { { SV2, SV1, SV0 },
2178                                      { SV3, SVALL, SV7 },
2179                                      { SV4, SV5, SV6 } };
2180
2181 #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
2182
2183 /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
2184 STATIC_OVL void
2185 set_seenv(lev, x0, y0, x, y)
2186 struct rm *lev;
2187 int x0, y0, x, y; /* from, to */
2188 {
2189     int dx = x - x0, dy = y0 - y;
2190
2191     lev->seenv |= seenv_matrix[sign(dy) + 1][sign(dx) + 1];
2192 }
2193
2194 /* Called by blackout(vault.c) when vault guard removes temporary corridor,
2195    turning spot <x0,y0> back into stone; <x1,y1> is an adjacent spot. */
2196 void
2197 unset_seenv(lev, x0, y0, x1, y1)
2198 struct rm *lev;     /* &levl[x1][y1] */
2199 int x0, y0, x1, y1; /* from, to; abs(x1-x0)==1 && abs(y0-y1)==1 */
2200 {
2201     int dx = x1 - x0, dy = y0 - y1;
2202
2203     lev->seenv &= ~seenv_matrix[dy + 1][dx + 1];
2204 }
2205
2206 /* ------------------------------------------------------------------------ */
2207
2208 /* T wall types, one for each row in wall_matrix[][]. */
2209 #define T_d 0
2210 #define T_l 1
2211 #define T_u 2
2212 #define T_r 3
2213
2214 /*
2215  * These are the column names of wall_matrix[][].  They are the "results"
2216  * of a tdwall pattern match.  All T walls are rotated so they become
2217  * a tdwall.  Then we do a single pattern match, but return the
2218  * correct result for the original wall by using different rows for
2219  * each of the wall types.
2220  */
2221 #define T_stone 0
2222 #define T_tlcorn 1
2223 #define T_trcorn 2
2224 #define T_hwall 3
2225 #define T_tdwall 4
2226
2227 static const int wall_matrix[4][5] = {
2228     { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
2229     { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
2230     { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
2231     { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
2232 };
2233
2234 /* Cross wall types, one for each "solid" quarter.  Rows of cross_matrix[][].
2235  */
2236 #define C_bl 0
2237 #define C_tl 1
2238 #define C_tr 2
2239 #define C_br 3
2240
2241 /*
2242  * These are the column names for cross_matrix[][].  They express results
2243  * in C_br (bottom right) terms.  All crosswalls with a single solid
2244  * quarter are rotated so the solid section is at the bottom right.
2245  * We pattern match on that, but return the correct result depending
2246  * on which row we'ere looking at.
2247  */
2248 #define C_trcorn 0
2249 #define C_brcorn 1
2250 #define C_blcorn 2
2251 #define C_tlwall 3
2252 #define C_tuwall 4
2253 #define C_crwall 5
2254
2255 static const int cross_matrix[4][6] = {
2256     { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
2257     { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
2258     { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
2259     { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
2260 };
2261
2262 /* Print out a T wall warning and all interesting info. */
2263 STATIC_OVL void
2264 t_warn(lev)
2265 struct rm *lev;
2266 {
2267     static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
2268     const char *wname;
2269
2270     if (lev->typ == TUWALL)
2271         wname = "tuwall";
2272     else if (lev->typ == TLWALL)
2273         wname = "tlwall";
2274     else if (lev->typ == TRWALL)
2275         wname = "trwall";
2276     else if (lev->typ == TDWALL)
2277         wname = "tdwall";
2278     else
2279         wname = "unknown";
2280     impossible(warn_str, wname, lev->wall_info & WM_MASK,
2281                (unsigned int) lev->seenv);
2282 }
2283
2284 /*
2285  * Return the correct graphics character index using wall type, wall mode,
2286  * and the seen vector.  It is expected that seenv is non zero.
2287  *
2288  * All T-wall vectors are rotated to be TDWALL.  All single crosswall
2289  * blocks are rotated to bottom right.  All double crosswall are rotated
2290  * to W_X_BLTR.  All results are converted back.
2291  *
2292  * The only way to understand this is to take out pen and paper and
2293  * draw diagrams.  See rm.h for more details on the wall modes and
2294  * seen vector (SV).
2295  */
2296 STATIC_OVL int
2297 wall_angle(lev)
2298 struct rm *lev;
2299 {
2300     register unsigned int seenv = lev->seenv & 0xff;
2301     const int *row;
2302     int col, idx;
2303
2304 #define only(sv, bits) (((sv) & (bits)) && !((sv) & ~(bits)))
2305     switch (lev->typ) {
2306     case TUWALL:
2307         row = wall_matrix[T_u];
2308         seenv = (seenv >> 4 | seenv << 4) & 0xff; /* rotate to tdwall */
2309         goto do_twall;
2310     case TLWALL:
2311         row = wall_matrix[T_l];
2312         seenv = (seenv >> 2 | seenv << 6) & 0xff; /* rotate to tdwall */
2313         goto do_twall;
2314     case TRWALL:
2315         row = wall_matrix[T_r];
2316         seenv = (seenv >> 6 | seenv << 2) & 0xff; /* rotate to tdwall */
2317         goto do_twall;
2318     case TDWALL:
2319         row = wall_matrix[T_d];
2320  do_twall:
2321         switch (lev->wall_info & WM_MASK) {
2322         case 0:
2323             if (seenv == SV4) {
2324                 col = T_tlcorn;
2325             } else if (seenv == SV6) {
2326                 col = T_trcorn;
2327             } else if (seenv & (SV3 | SV5 | SV7)
2328                        || ((seenv & SV4) && (seenv & SV6))) {
2329                 col = T_tdwall;
2330             } else if (seenv & (SV0 | SV1 | SV2)) {
2331                 col = (seenv & (SV4 | SV6) ? T_tdwall : T_hwall);
2332             } else {
2333                 t_warn(lev);
2334                 col = T_stone;
2335             }
2336             break;
2337         case WM_T_LONG:
2338             if (seenv & (SV3 | SV4) && !(seenv & (SV5 | SV6 | SV7))) {
2339                 col = T_tlcorn;
2340             } else if (seenv & (SV6 | SV7) && !(seenv & (SV3 | SV4 | SV5))) {
2341                 col = T_trcorn;
2342             } else if ((seenv & SV5)
2343                        || ((seenv & (SV3 | SV4)) && (seenv & (SV6 | SV7)))) {
2344                 col = T_tdwall;
2345             } else {
2346                 /* only SV0|SV1|SV2 */
2347                 if (!only(seenv, SV0 | SV1 | SV2))
2348                     t_warn(lev);
2349                 col = T_stone;
2350             }
2351             break;
2352         case WM_T_BL:
2353 #if 0  /* older method, fixed */
2354             if (only(seenv, SV4 | SV5)) {
2355                 col = T_tlcorn;
2356             } else if ((seenv & (SV0 | SV1 | SV2))
2357                        && only(seenv, SV0 | SV1 | SV2 | SV6 | SV7)) {
2358                 col = T_hwall;
2359             } else if ((seenv & SV3)
2360                        || ((seenv & (SV0 | SV1 | SV2))
2361                            && (seenv & (SV4 | SV5)))) {
2362                 col = T_tdwall;
2363             } else {
2364                 if (seenv != SV6)
2365                     t_warn(lev);
2366                 col = T_stone;
2367             }
2368 #endif /* 0 */
2369             if (only(seenv, SV4 | SV5))
2370                 col = T_tlcorn;
2371             else if ((seenv & (SV0 | SV1 | SV2 | SV7))
2372                      && !(seenv & (SV3 | SV4 | SV5)))
2373                 col = T_hwall;
2374             else if (only(seenv, SV6))
2375                 col = T_stone;
2376             else
2377                 col = T_tdwall;
2378             break;
2379         case WM_T_BR:
2380 #if 0  /* older method, fixed */
2381             if (only(seenv, SV5 | SV6)) {
2382                 col = T_trcorn;
2383             } else if ((seenv & (SV0 | SV1 | SV2))
2384                        && only(seenv, SV0 | SV1 | SV2 | SV3 | SV4)) {
2385                 col = T_hwall;
2386             } else if ((seenv & SV7)
2387                        || ((seenv & (SV0 | SV1 | SV2))
2388                            && (seenv & (SV5 | SV6)))) {
2389                 col = T_tdwall;
2390             } else {
2391                 if (seenv != SV4)
2392                     t_warn(lev);
2393                 col = T_stone;
2394             }
2395 #endif /* 0 */
2396             if (only(seenv, SV5 | SV6))
2397                 col = T_trcorn;
2398             else if ((seenv & (SV0 | SV1 | SV2 | SV3))
2399                      && !(seenv & (SV5 | SV6 | SV7)))
2400                 col = T_hwall;
2401             else if (only(seenv, SV4))
2402                 col = T_stone;
2403             else
2404                 col = T_tdwall;
2405
2406             break;
2407         default:
2408             impossible("wall_angle: unknown T wall mode %d",
2409                        lev->wall_info & WM_MASK);
2410             col = T_stone;
2411             break;
2412         }
2413         idx = row[col];
2414         break;
2415
2416     case SDOOR:
2417         if (lev->horizontal)
2418             goto horiz;
2419         /*FALLTHRU*/
2420     case VWALL:
2421         switch (lev->wall_info & WM_MASK) {
2422         case 0:
2423             idx = seenv ? S_vwall : S_stone;
2424             break;
2425         case 1:
2426             idx = seenv & (SV1 | SV2 | SV3 | SV4 | SV5) ? S_vwall : S_stone;
2427             break;
2428         case 2:
2429             idx = seenv & (SV0 | SV1 | SV5 | SV6 | SV7) ? S_vwall : S_stone;
2430             break;
2431         default:
2432             impossible("wall_angle: unknown vwall mode %d",
2433                        lev->wall_info & WM_MASK);
2434             idx = S_stone;
2435             break;
2436         }
2437         break;
2438
2439     case HWALL:
2440  horiz:
2441         switch (lev->wall_info & WM_MASK) {
2442         case 0:
2443             idx = seenv ? S_hwall : S_stone;
2444             break;
2445         case 1:
2446             idx = seenv & (SV3 | SV4 | SV5 | SV6 | SV7) ? S_hwall : S_stone;
2447             break;
2448         case 2:
2449             idx = seenv & (SV0 | SV1 | SV2 | SV3 | SV7) ? S_hwall : S_stone;
2450             break;
2451         default:
2452             impossible("wall_angle: unknown hwall mode %d",
2453                        lev->wall_info & WM_MASK);
2454             idx = S_stone;
2455             break;
2456         }
2457         break;
2458
2459 #define set_corner(idx, lev, which, outer, inner, name)    \
2460     switch ((lev)->wall_info & WM_MASK) {                  \
2461     case 0:                                                \
2462         idx = which;                                       \
2463         break;                                             \
2464     case WM_C_OUTER:                                       \
2465         idx = seenv & (outer) ? which : S_stone;           \
2466         break;                                             \
2467     case WM_C_INNER:                                       \
2468         idx = seenv & ~(inner) ? which : S_stone;          \
2469         break;                                             \
2470     default:                                               \
2471         impossible("wall_angle: unknown %s mode %d", name, \
2472                    (lev)->wall_info &WM_MASK);             \
2473         idx = S_stone;                                     \
2474         break;                                             \
2475     }
2476
2477     case TLCORNER:
2478         set_corner(idx, lev, S_tlcorn, (SV3 | SV4 | SV5), SV4, "tlcorn");
2479         break;
2480     case TRCORNER:
2481         set_corner(idx, lev, S_trcorn, (SV5 | SV6 | SV7), SV6, "trcorn");
2482         break;
2483     case BLCORNER:
2484         set_corner(idx, lev, S_blcorn, (SV1 | SV2 | SV3), SV2, "blcorn");
2485         break;
2486     case BRCORNER:
2487         set_corner(idx, lev, S_brcorn, (SV7 | SV0 | SV1), SV0, "brcorn");
2488         break;
2489
2490     case CROSSWALL:
2491         switch (lev->wall_info & WM_MASK) {
2492         case 0:
2493             if (seenv == SV0)
2494                 idx = S_brcorn;
2495             else if (seenv == SV2)
2496                 idx = S_blcorn;
2497             else if (seenv == SV4)
2498                 idx = S_tlcorn;
2499             else if (seenv == SV6)
2500                 idx = S_trcorn;
2501             else if (!(seenv & ~(SV0 | SV1 | SV2))
2502                      && (seenv & SV1 || seenv == (SV0 | SV2)))
2503                 idx = S_tuwall;
2504             else if (!(seenv & ~(SV2 | SV3 | SV4))
2505                      && (seenv & SV3 || seenv == (SV2 | SV4)))
2506                 idx = S_trwall;
2507             else if (!(seenv & ~(SV4 | SV5 | SV6))
2508                      && (seenv & SV5 || seenv == (SV4 | SV6)))
2509                 idx = S_tdwall;
2510             else if (!(seenv & ~(SV0 | SV6 | SV7))
2511                      && (seenv & SV7 || seenv == (SV0 | SV6)))
2512                 idx = S_tlwall;
2513             else
2514                 idx = S_crwall;
2515             break;
2516
2517         case WM_X_TL:
2518             row = cross_matrix[C_tl];
2519             seenv = (seenv >> 4 | seenv << 4) & 0xff;
2520             goto do_crwall;
2521         case WM_X_TR:
2522             row = cross_matrix[C_tr];
2523             seenv = (seenv >> 6 | seenv << 2) & 0xff;
2524             goto do_crwall;
2525         case WM_X_BL:
2526             row = cross_matrix[C_bl];
2527             seenv = (seenv >> 2 | seenv << 6) & 0xff;
2528             goto do_crwall;
2529         case WM_X_BR:
2530             row = cross_matrix[C_br];
2531  do_crwall:
2532             if (seenv == SV4) {
2533                 idx = S_stone;
2534             } else {
2535                 seenv = seenv & ~SV4; /* strip SV4 */
2536                 if (seenv == SV0) {
2537                     col = C_brcorn;
2538                 } else if (seenv & (SV2 | SV3)) {
2539                     if (seenv & (SV5 | SV6 | SV7))
2540                         col = C_crwall;
2541                     else if (seenv & (SV0 | SV1))
2542                         col = C_tuwall;
2543                     else
2544                         col = C_blcorn;
2545                 } else if (seenv & (SV5 | SV6)) {
2546                     if (seenv & (SV1 | SV2 | SV3))
2547                         col = C_crwall;
2548                     else if (seenv & (SV0 | SV7))
2549                         col = C_tlwall;
2550                     else
2551                         col = C_trcorn;
2552                 } else if (seenv & SV1) {
2553                     col = seenv & SV7 ? C_crwall : C_tuwall;
2554                 } else if (seenv & SV7) {
2555                     col = seenv & SV1 ? C_crwall : C_tlwall;
2556                 } else {
2557                     impossible("wall_angle: bottom of crwall check");
2558                     col = C_crwall;
2559                 }
2560
2561                 idx = row[col];
2562             }
2563             break;
2564
2565         case WM_X_TLBR:
2566             if (only(seenv, SV1 | SV2 | SV3))
2567                 idx = S_blcorn;
2568             else if (only(seenv, SV5 | SV6 | SV7))
2569                 idx = S_trcorn;
2570             else if (only(seenv, SV0 | SV4))
2571                 idx = S_stone;
2572             else
2573                 idx = S_crwall;
2574             break;
2575
2576         case WM_X_BLTR:
2577             if (only(seenv, SV0 | SV1 | SV7))
2578                 idx = S_brcorn;
2579             else if (only(seenv, SV3 | SV4 | SV5))
2580                 idx = S_tlcorn;
2581             else if (only(seenv, SV2 | SV6))
2582                 idx = S_stone;
2583             else
2584                 idx = S_crwall;
2585             break;
2586
2587         default:
2588             impossible("wall_angle: unknown crosswall mode");
2589             idx = S_stone;
2590             break;
2591         }
2592         break;
2593
2594     default:
2595         impossible("wall_angle: unexpected wall type %d", lev->typ);
2596         idx = S_stone;
2597     }
2598     return idx;
2599 }
2600
2601 /*display.c*/