OSDN Git Service

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