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