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
121STATIC_DCL void FDECL(display_monster,(XCHAR_P,XCHAR_P,struct monst *,int,XCHAR_P));
122STATIC_DCL int FDECL(swallow_to_glyph, (int, int));
123STATIC_DCL void FDECL(display_warning,(struct monst *));
124
125STATIC_DCL int FDECL(check_pos, (int, int, int));
126#ifdef WA_VERBOSE
127STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
128#endif
129STATIC_DCL int FDECL(set_twall, (int,int, int,int, int,int, int,int));
130STATIC_DCL int FDECL(set_wall, (int, int, int));
131STATIC_DCL int FDECL(set_corn, (int,int, int,int, int,int, int,int));
132STATIC_DCL int FDECL(set_crosswall, (int, int));
133STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int));
134STATIC_DCL void FDECL(t_warn, (struct rm *));
135STATIC_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 */
147struct obj *
148vobj_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 */
167void
168magic_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 */
212void
213map_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 */
230void
231map_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 */
249void
250map_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 */
271void
272map_invisible(x, y)
273register 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 */
291void
292unmap_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
336void
337map_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 */
358STATIC_OVL void
359display_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 */
455STATIC_OVL void
456display_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 */
488void
489feel_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 */
628void
629newsym(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 {
763show_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 */
777void
778shieldeff(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
820static 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
828void
829tmp_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 */
920void
921swallowed(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 */
978void
979under_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 */
1023void
1024under_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 */
1064void
1065see_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 */
1086void
1087set_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 */
1109void
1110see_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 */
1120void
1121see_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 */
1136void
1137curs_on_u()
1138{
1139    flush_screen(1);	/* Flush waiting glyphs & put cursor on hero */
1140}
1141
1142int
1143doredraw()
1144{
1145    docrt();
1146    return 0;
1147}
1148
1149void
1150docrt()
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
1201typedef struct {
1202    xchar new;		/* perhaps move this bit into the rm strucure. */
1203    int   glyph;
1204} gbuf_entry;
1205
1206static gbuf_entry gbuf[ROWNO][COLNO];
1207static char gbuf_start[ROWNO];
1208static char gbuf_stop[ROWNO];
1209
1210/*
1211 * Store the glyph in the 3rd screen for later flushing.
1212 */
1213void
1214show_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
1293static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1294/*
1295 * Turn the 3rd screen into stone.
1296 */
1297void
1298clear_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 */
1315void
1316row_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
1326void
1327cls()
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 */
1339void
1340flush_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 */
1388int
1389back_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 */
1485STATIC_OVL int
1486swallow_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 */
1511int
1512zapdir_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 */
1531int
1532glyph_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
1548static const char *FDECL(type_to_name, (int));
1549static void FDECL(error4, (int,int,int,int,int,int));
1550
1551static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1552static 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
1565static const char *
1566type_to_name(type)
1567    int type;
1568{
1569    return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type];
1570}
1571
1572static void
1573error4(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 */
1589STATIC_OVL int
1590check_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
1603STATIC_OVL boolean
1604more_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. */
1618STATIC_OVL int
1619set_twall(x0,y0, x1,y1, x2,y2, x3,y3)
1620int 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. */
1636STATIC_OVL int
1637set_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. */
1659STATIC_OVL int
1660set_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. */
1688STATIC_OVL int
1689set_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. */
1724void
1725set_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. */
1799unsigned 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). */
1806STATIC_OVL void
1807set_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
1836static 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
1864static 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. */
1873STATIC_OVL void
1874t_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 */
1902STATIC_OVL int
1903wall_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];
1926do_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:
2041horiz:
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];
2125do_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*/
2197