OSDN Git Service

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