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