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