OSDN Git Service

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