1 /* NetHack 3.6 display.c $NHDT-Date: 1574882660 2019/11/27 19:24:20 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.108 $ */
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. */
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
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
33 * If the location is in sight, display in order:
34 * visible (or sensed) monsters
39 * If the location is out of sight, display in order:
40 * sensed monsters (via telepathy or persistent detection)
41 * warning (partly-sensed monster shown as an abstraction)
44 * "Remembered, unseen monster" is handled like an object rather
45 * than a monster, and stays displayed whether or not it is in sight.
46 * It is removed when a visible or sensed or warned-of monster gets
47 * shown at its location or when searching or fighting reveals that
48 * no monster is there.
51 * Here is a list of the major routines in this file to be used externally:
55 * Possibly update the screen location (x,y). This is the workhorse routine.
56 * It is always correct --- where correct means following the in-sight/out-
57 * of-sight rules. **Most of the code should use this routine.** This
58 * routine updates the map and displays monsters.
67 * If you absolutely must override the in-sight/out-of-sight rules, there
68 * are two possibilities. First, you can mess with vision to force the
69 * location in sight then use newsym(), or you can use the map_* routines.
70 * The first has not been tried [no need] and the second is used in the
71 * detect routines --- detect object, magic mapping, etc. The map_*
72 * routines *change* what the hero remembers. All changes made by these
73 * routines will be sticky --- they will survive screen redraws. Do *not*
74 * use these for things that only temporarily change the screen. These
75 * routines are also used directly by newsym(). unmap_object is used to
76 * clear a remembered object when/if detection reveals it isn't there.
81 * This is direct (no processing in between) buffered access to the screen.
82 * Temporary screen effects are run through this and its companion,
83 * flush_screen(). There is yet a lower level routine, print_glyph(),
84 * but this is unbuffered and graphic dependent (i.e. it must be surrounded
85 * by graphic set-up and tear-down routines). Do not use print_glyph().
92 * These are only used when something affects all of the monsters or
93 * objects or traps. For objects and traps, the only thing is hallucination.
94 * For monsters, there are hallucination and changing from/to blindness, etc.
99 * This is a useful interface for displaying temporary items on the screen.
100 * Its interface is different than previously, so look at it carefully.
104 * Parts of the rm structure that are used:
106 * typ - What is really there.
107 * glyph - What the hero remembers. This will never be a monster.
108 * Monsters "float" above this.
109 * lit - True if the position is lit. An optimization for
111 * waslit - True if the position was *remembered* as lit.
112 * seenv - A vector of bits representing the directions from which the
113 * hero has seen this position. The vector's primary use is
114 * determining how walls are seen. E.g. a wall sometimes looks
115 * like stone on one side, but is seen as wall from the other.
116 * Other uses are for unmapping detected objects and felt
117 * locations, where we need to know if the hero has ever
119 * flags - Additional information for the typ field. Different for
121 * horizontal - Indicates whether the wall or door is horizontal or
126 STATIC_DCL void FDECL(show_mon_or_warn, (int, int, int));
127 STATIC_DCL void FDECL(display_monster,
128 (XCHAR_P, XCHAR_P, struct monst *, int, XCHAR_P));
129 STATIC_DCL int FDECL(swallow_to_glyph, (int, int));
130 STATIC_DCL void FDECL(display_warning, (struct monst *));
132 STATIC_DCL int FDECL(check_pos, (int, int, int));
133 STATIC_DCL int FDECL(get_bk_glyph, (XCHAR_P, XCHAR_P));
134 STATIC_DCL int FDECL(tether_glyph, (int, int));
136 /*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
138 STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
141 STATIC_DCL int FDECL(set_twall, (int, int, int, int, int, int, int, int));
142 STATIC_DCL int FDECL(set_wall, (int, int, int));
143 STATIC_DCL int FDECL(set_corn, (int, int, int, int, int, int, int, int));
144 STATIC_DCL int FDECL(set_crosswall, (int, int));
145 STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int));
146 STATIC_DCL void FDECL(t_warn, (struct rm *));
147 STATIC_DCL int FDECL(wall_angle, (struct rm *));
149 #define remember_topology(x, y) (lastseentyp[x][y] = levl[x][y].typ)
152 * magic_map_background()
154 * This function is similar to map_background (see below) except we pay
155 * attention to and correct unexplored, lit ROOM and CORR spots.
158 magic_map_background(x, y, show)
162 int glyph = back_to_glyph(x, y); /* assumes hero can see x,y */
163 struct rm *lev = &levl[x][y];
166 * Correct for out of sight lit corridors and rooms that the hero
167 * doesn't remember as lit.
169 if (!cansee(x, y) && !lev->waslit) {
170 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
171 if (lev->typ == ROOM && glyph == cmap_to_glyph(S_room))
172 glyph = cmap_to_glyph((flags.dark_room && iflags.use_color)
175 else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
176 glyph = cmap_to_glyph(S_corr);
178 if (level.flags.hero_memory)
181 show_glyph(x, y, glyph);
183 remember_topology(x, y);
187 * The routines map_background(), map_object(), and map_trap() could just
190 * map_glyph(x,y,glyph,show)
192 * Which is called with the xx_to_glyph() in the call. Then I can get
193 * rid of 3 routines that don't do very much anyway. And then stop
194 * having to create fake objects and traps. However, I am reluctant to
197 /* FIXME: some of these use xchars for x and y, and some use ints. Make
204 * Make the real background part of our map. This routine assumes that
205 * the hero can physically see the location. Update the screen if directed.
208 map_background(x, y, show)
212 register int glyph = back_to_glyph(x, y);
214 if (level.flags.hero_memory)
215 levl[x][y].glyph = glyph;
217 show_glyph(x, y, glyph);
223 * Map the trap and print it out if directed. This routine assumes that the
224 * hero can physically see the location.
228 register struct trap *trap;
231 register int x = trap->tx, y = trap->ty;
232 register int glyph = trap_to_glyph(trap, newsym_rn2);
234 if (level.flags.hero_memory)
235 levl[x][y].glyph = glyph;
237 show_glyph(x, y, glyph);
243 * Map the given object. This routine assumes that the hero can physically
244 * see the location of the object. Update the screen if directed.
247 map_object(obj, show)
248 register struct obj *obj;
251 register int x = obj->ox, y = obj->oy;
252 register int glyph = obj_to_glyph(obj, newsym_rn2);
254 if (level.flags.hero_memory) {
255 /* MRKR: While hallucinating, statues are seen as random monsters */
256 /* but remembered as random objects. */
258 if (Hallucination && obj->otyp == STATUE) {
259 levl[x][y].glyph = random_obj_to_glyph(newsym_rn2);
261 levl[x][y].glyph = glyph;
265 show_glyph(x, y, glyph);
271 * Make the hero remember that a square contains an invisible monster.
272 * This is a special case in that the square will continue to be displayed
273 * this way even when the hero is close enough to see it. To get rid of
274 * this and display the square's actual contents, use unmap_object() followed
275 * by newsym() if necessary.
281 if (x != u.ux || y != u.uy) { /* don't display I at hero's location */
282 if (level.flags.hero_memory)
283 levl[x][y].glyph = GLYPH_INVISIBLE;
284 show_glyph(x, y, GLYPH_INVISIBLE);
289 unmap_invisible(x, y)
292 if (isok(x,y) && glyph_is_invisible(levl[x][y].glyph)) {
304 * Remove something from the map when the hero realizes it's not there any
305 * more. Replace it with background or known trap, but not with any other
306 * If this is used for detection, a full screen update is imminent anyway;
307 * if this is used to get rid of an invisible monster notation, we might have
314 register struct trap *trap;
316 if (!level.flags.hero_memory)
319 if ((trap = t_at(x, y)) != 0 && trap->tseen && !covers_traps(x, y)) {
321 } else if (levl[x][y].seenv) {
322 struct rm *lev = &levl[x][y];
324 map_background(x, y, 0);
326 /* turn remembered dark room squares dark */
327 if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room)
329 lev->glyph = cmap_to_glyph(S_stone);
331 levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
338 * Make whatever at this location show up. This is only for non-living
339 * things. This will not handle feeling invisible objects correctly.
341 * Internal to display.c, this is a #define for speed.
343 #define _map_location(x, y, show) \
345 register struct obj *obj; \
346 register struct trap *trap; \
348 if ((obj = vobj_at(x, y)) && !covers_objects(x, y)) \
349 map_object(obj, show); \
350 else if ((trap = t_at(x, y)) && trap->tseen && !covers_traps(x, y)) \
351 map_trap(trap, show); \
353 map_background(x, y, show); \
355 remember_topology(x, y); \
359 map_location(x, y, show)
362 _map_location(x, y, show);
365 /* display something on monster layer; may need to fixup object layer */
367 show_mon_or_warn(x, y, monglyph)
372 /* "remembered, unseen monster" is tracked by object layer so if we're
373 putting something on monster layer at same spot, stop remembering
374 that; if an object is in view there, start remembering it instead */
375 if (glyph_is_invisible(levl[x][y].glyph)) {
377 if (cansee(x, y) && (o = vobj_at(x, y)) != 0)
378 map_object(o, FALSE);
381 show_glyph(x, y, monglyph);
385 #define PHYSICALLY_SEEN 1
386 #define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my)))
391 * Note that this is *not* a map_XXXX() function! Monsters sort of float
394 * Yuck. Display body parts by recognizing that the display position is
395 * not the same as the monster position. Currently the only body part is
400 display_monster(x, y, mon, sightflags, worm_tail)
401 register xchar x, y; /* display position */
402 register struct monst *mon; /* monster to display */
403 int sightflags; /* 1 if the monster is physically seen;
404 2 if detected using Detect_monsters */
405 xchar worm_tail; /* mon is actually a worm tail */
407 boolean mon_mimic = (M_AP_TYPE(mon) != M_AP_NOTHING);
408 int sensed = (mon_mimic && (Protection_from_shape_changers
411 * We must do the mimic check first. If the mimic is mimicing something,
412 * and the location is in sight, we have to change the hero's memory
413 * so that when the position is out of sight, the hero remembers what
414 * the mimic was mimicing.
417 if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) {
418 switch (M_AP_TYPE(mon)) {
420 impossible("display_monster: bad m_ap_type value [ = %d ]",
421 (int) mon->m_ap_type);
424 show_glyph(x, y, mon_to_glyph(mon, newsym_rn2));
427 case M_AP_FURNITURE: {
429 * This is a poor man's version of map_background(). I can't
430 * use map_background() because we are overriding what is in
431 * the 'typ' field. Maybe have map_background()'s parameters
432 * be (x,y,glyph) instead of just (x,y).
434 * mappearance is currently set to an S_ index value in
437 int sym = mon->mappearance, glyph = cmap_to_glyph(sym);
439 levl[x][y].glyph = glyph;
441 show_glyph(x, y, glyph);
442 /* override real topology with mimic's fake one */
443 lastseentyp[x][y] = cmap_to_type(sym);
449 /* Make a fake object to send to map_object(). */
455 obj.otyp = mon->mappearance;
456 /* might be mimicing a corpse or statue */
457 obj.corpsenm = has_mcorpsenm(mon) ? MCORPSENM(mon) : PM_TENGU;
458 map_object(&obj, !sensed);
464 monnum_to_glyph(what_mon((int) mon->mappearance,
465 rn2_on_display_rng)));
470 /* If mimic is unsuccessfully mimicing something, display the monster. */
471 if (!mon_mimic || sensed) {
474 /* [ALI] Only use detected glyphs when monster wouldn't be
475 * visible by any other means.
477 * There are no glyphs for "detected pets" so we have to
478 * decide whether to display such things as detected or as tame.
479 * If both are being highlighted in the same way, it doesn't
480 * matter, but if not, showing them as pets is preferrable.
482 if (mon->mtame && !Hallucination) {
484 num = petnum_to_glyph(PM_LONG_WORM_TAIL);
486 num = pet_to_glyph(mon, rn2_on_display_rng);
487 } else if (sightflags == DETECTED) {
489 num = detected_monnum_to_glyph(
490 what_mon(PM_LONG_WORM_TAIL, rn2_on_display_rng));
492 num = detected_mon_to_glyph(mon, rn2_on_display_rng);
495 num = monnum_to_glyph(
496 what_mon(PM_LONG_WORM_TAIL, rn2_on_display_rng));
498 num = mon_to_glyph(mon, rn2_on_display_rng);
500 show_mon_or_warn(x, y, num);
507 * This is also *not* a map_XXXX() function! Monster warnings float
508 * above everything just like monsters do, but only if the monster
511 * Do not call for worm tails.
515 register struct monst *mon;
517 int x = mon->mx, y = mon->my;
520 if (mon_warning(mon)) {
521 int wl = Hallucination ?
522 rn2_on_display_rng(WARNCOUNT - 1) + 1 : warning_of(mon);
523 glyph = warning_to_glyph(wl);
524 } else if (MATCH_WARN_OF_MON(mon)) {
525 glyph = mon_to_glyph(mon, rn2_on_display_rng);
527 impossible("display_warning did not match warning type?");
530 show_mon_or_warn(x, y, glyph);
539 if (mon_warning(mon)) {
540 tmp = (int) (mon->m_lev / 4); /* match display.h */
541 wl = (tmp > WARNCOUNT - 1) ? WARNCOUNT - 1 : tmp;
550 * When hero knows what happened to location, even when blind.
566 * Feel the given location. This assumes that the hero is blind and that
567 * the given position is either the hero's or one of the eight squares
568 * adjacent to the hero (except for a boulder push).
569 * If an invisible monster has gone away, that will be discovered. If an
570 * invisible monster has appeared, this will _not_ be discovered since
571 * searching only finds one monster per turn so we must check that separately.
579 register struct monst *mon;
584 /* If hero's memory of an invisible monster is accurate, we want to keep
585 * him from detecting the same monster over and over again on each turn.
586 * We must return (so we don't erase the monster). (We must also, in the
587 * search function, be sure to skip over previously detected 'I's.)
589 if (glyph_is_invisible(lev->glyph) && m_at(x, y))
592 /* The hero can't feel non pool locations while under water
593 except for lava and ice. */
594 if (Underwater && !Is_waterlevel(&u.uz)
595 && !is_pool_or_lava(x, y) && !is_ice(x, y))
598 /* Set the seen vector as if the hero had seen it.
599 It doesn't matter if the hero is levitating or not. */
600 set_seenv(lev, u.ux, u.uy, x, y);
602 if (!can_reach_floor(FALSE)) {
604 * Levitation Rules. It is assumed that the hero can feel the state
605 * of the walls around herself and can tell if she is in a corridor,
606 * room, or doorway. Boulders are felt because they are large enough.
607 * Anything else is unknown because the hero can't reach the ground.
608 * This makes things difficult.
610 * Check (and display) in order:
612 * + Stone, walls, and closed doors.
613 * + Boulders. [see a boulder before a doorway]
615 * + Room/water positions
616 * + Everything else (hallways!)
618 if (IS_ROCK(lev->typ)
619 || (IS_DOOR(lev->typ)
620 && (lev->doormask & (D_LOCKED | D_CLOSED)))) {
621 map_background(x, y, 1);
622 } else if ((boulder = sobj_at(BOULDER, x, y)) != 0) {
623 map_object(boulder, 1);
624 } else if (IS_DOOR(lev->typ)) {
625 map_background(x, y, 1);
626 } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
627 boolean do_room_glyph;
630 * An open room or water location. Normally we wouldn't touch
631 * this, but we have to get rid of remembered boulder symbols.
632 * This will only occur in rare occasions when the hero goes
633 * blind and doesn't find a boulder where expected (something
634 * came along and picked it up). We know that there is not a
635 * boulder at this location. Show fountains, pools, etc.
636 * underneath if already seen. Otherwise, show the appropriate
639 * Similarly, if the hero digs a hole in a wall or feels a
640 * location that used to contain an unseen monster. In these
641 * cases, there's no reason to assume anything was underneath,
642 * so just show the appropriate floor symbol. If something was
643 * embedded in the wall, the glyph will probably already
644 * reflect that. Don't change the symbol in this case.
646 * This isn't quite correct. If the boulder was on top of some
647 * other objects they should be seen once the boulder is removed.
648 * However, we have no way of knowing that what is there now
649 * was there then. So we let the hero have a lapse of memory.
650 * We could also just display what is currently on the top of the
651 * object stack (if anything).
653 do_room_glyph = FALSE;
654 if (lev->glyph == objnum_to_glyph(BOULDER)
655 || glyph_is_invisible(lev->glyph)) {
656 if (lev->typ != ROOM && lev->seenv)
657 map_background(x, y, 1);
659 do_room_glyph = TRUE;
660 } else if (lev->glyph >= cmap_to_glyph(S_stone)
661 && lev->glyph < cmap_to_glyph(S_darkroom)) {
662 do_room_glyph = TRUE;
665 lev->glyph = (flags.dark_room && iflags.use_color
666 && !Is_rogue_level(&u.uz))
667 ? cmap_to_glyph(S_darkroom)
668 : (lev->waslit ? cmap_to_glyph(S_room)
669 : cmap_to_glyph(S_stone));
670 show_glyph(x, y, lev->glyph);
673 /* We feel it (I think hallways are the only things left). */
674 map_background(x, y, 1);
675 /* Corridors are never felt as lit (unless remembered that way) */
676 /* (lit_corridor only). */
677 if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
679 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
680 else if (lev->typ == ROOM && flags.dark_room && iflags.use_color
681 && lev->glyph == cmap_to_glyph(S_room))
682 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_darkroom));
685 _map_location(x, y, 1);
689 * A ball or chain is only felt if it is first on the object
690 * location list. Otherwise, we need to clear the felt bit ---
691 * something has been dropped on the ball/chain. If the bit is
692 * not cleared, then when the ball/chain is moved it will drop
695 if (uchain->ox == x && uchain->oy == y) {
696 if (level.objects[x][y] == uchain)
697 u.bc_felt |= BC_CHAIN;
699 u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
701 if (!carried(uball) && uball->ox == x && uball->oy == y) {
702 if (level.objects[x][y] == uball)
703 u.bc_felt |= BC_BALL;
705 u.bc_felt &= ~BC_BALL; /* do not feel the ball */
709 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
710 if (lev->typ == ROOM && lev->glyph == cmap_to_glyph(S_room)
711 && (!lev->waslit || (flags.dark_room && iflags.use_color)))
712 show_glyph(x, y, lev->glyph = cmap_to_glyph(
713 flags.dark_room ? S_darkroom : S_stone));
714 else if (lev->typ == CORR && lev->glyph == cmap_to_glyph(S_litcorr)
716 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
718 /* draw monster on top if we can sense it */
719 if ((x != u.ux || y != u.uy) && (mon = m_at(x, y)) != 0 && sensemon(mon))
720 display_monster(x, y, mon,
721 (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon))
730 * Possibly put a new glyph at the given location.
736 register struct monst *mon;
737 register struct rm *lev = &(levl[x][y]);
739 register xchar worm_tail;
743 #ifdef HANGUPHANDLING
744 if (program_state.done_hup)
748 /* only permit updating the hero when swallowed */
750 if (x == u.ux && y == u.uy)
754 if (Underwater && !Is_waterlevel(&u.uz)) {
755 /* when underwater, don't do anything unless <x,y> is an
756 adjacent water or lava or ice position */
757 if (!(is_pool_or_lava(x, y) || is_ice(x, y)) || distu(x, y) > 2)
761 /* Can physically see the location. */
763 NhRegion *reg = visible_region_at(x, y);
765 * Don't use templit here: E.g.
767 * lev->waslit = !!(lev->lit || templit(x,y));
769 * Otherwise we have the "light pool" problem, where non-permanently
770 * lit areas just out of sight stay remembered as lit. They should
773 * Perhaps ALL areas should revert to their "unlit" look when
776 lev->waslit = (lev->lit != 0); /* remember lit condition */
779 worm_tail = is_worm_tail(mon);
782 * Normal region shown only on accessible positions, but
783 * poison clouds also shown above lava, pools and moats.
784 * However, sensed monsters take precedence over all regions.
787 && (ACCESSIBLE(lev->typ)
788 || (reg->glyph == cmap_to_glyph(S_poisoncloud)
789 && is_pool_or_lava(x, y)))
790 && (!mon || worm_tail || !sensemon(mon))) {
791 show_region(reg, x, y);
795 if (x == u.ux && y == u.uy) {
796 int see_self = canspotself();
798 /* update map information for <u.ux,u.uy> (remembered topology
799 and object/known trap/terrain glyph) but only display it if
800 hero can't see him/herself, then show self if appropriate */
801 _map_location(x, y, !see_self);
805 see_it = mon && (mon_visible(mon)
806 || (!worm_tail && (tp_sensemon(mon)
807 || MATCH_WARN_OF_MON(mon))));
808 if (mon && (see_it || (!worm_tail && Detect_monsters))) {
810 struct trap *trap = t_at(x, y);
811 int tt = trap ? trap->ttyp : NO_TRAP;
813 /* if monster is in a physical trap, you see trap too */
814 if (tt == BEAR_TRAP || is_pit(tt) || tt == WEB)
817 _map_location(x, y, 0); /* map under the monster */
818 /* also gets rid of any invisibility glyph */
819 display_monster(x, y, mon,
820 see_it ? PHYSICALLY_SEEN : DETECTED,
822 } else if (mon && mon_warning(mon) && !is_worm_tail(mon)) {
823 display_warning(mon);
824 } else if (glyph_is_invisible(lev->glyph)) {
827 _map_location(x, y, 1); /* map the location */\
830 /* Can't see the location. */
832 if (x == u.ux && y == u.uy) {
833 feel_location(u.ux, u.uy); /* forces an update */
837 } else if ((mon = m_at(x, y)) != 0
838 && ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)
839 || (see_with_infrared(mon)
840 && mon_visible(mon)))) != 0
841 || Detect_monsters)) {
842 /* Seen or sensed monsters are printed every time.
843 This also gets rid of any invisibility glyph. */
844 display_monster(x, y, mon, see_it ? 0 : DETECTED,
845 is_worm_tail(mon) ? TRUE : FALSE);
846 } else if (mon && mon_warning(mon) && !is_worm_tail(mon)) {
847 display_warning(mon);
850 * If the location is remembered as being both dark (waslit is false)
851 * and lit (glyph is a lit room or lit corridor) then it was either:
853 * (1) A dark location that the hero could see through night
855 * (2) Darkened while out of the hero's sight. This can happen
856 * when cursed scroll of light is read.
858 * In either case, we have to manually correct the hero's memory to
859 * match waslit. Deciding when to change waslit is non-trivial.
861 * Note: If flags.lit_corridor is set, then corridors act like room
862 * squares. That is, they light up if in night vision range.
863 * If flags.lit_corridor is not set, then corridors will
864 * remain dark unless lit by a light spell and may darken
865 * again, as discussed above.
867 * These checks and changes must be here and not in back_to_glyph().
868 * They are dependent on the position being out of sight.
870 } else if (Is_rogue_level(&u.uz)) {
871 if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
872 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
873 else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM
875 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone));
878 } else if (!lev->waslit || (flags.dark_room && iflags.use_color)) {
879 if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
880 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
881 else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM)
882 show_glyph(x, y, lev->glyph = cmap_to_glyph(DARKROOMSYM));
887 show_glyph(x, y, lev->glyph);
897 * Put magic shield pyrotechnics at the given location. This *could* be
898 * pulled into a platform dependent routine for fancier graphics if desired.
908 if (cansee(x, y)) { /* Don't see anything if can't see the location */
909 for (i = 0; i < SHIELD_COUNT; i++) {
910 show_glyph(x, y, cmap_to_glyph(shield_static[i]));
911 flush_screen(1); /* make sure the glyph shows up */
914 newsym(x, y); /* restore the old information */
925 return zapdir_to_glyph(sgn(tdx),sgn(tdy), 2);
931 * Temporarily place glyphs on the screen. Do not call delay_output(). It
932 * is up to the caller to decide if it wants to wait [presently, everyone
933 * but explode() wants to delay].
936 * (DISP_BEAM, glyph) open, initialize glyph
937 * (DISP_FLASH, glyph) open, initialize glyph
938 * (DISP_ALWAYS, glyph) open, initialize glyph
939 * (DISP_CHANGE, glyph) change glyph
940 * (DISP_END, 0) close & clean up (2nd argument doesn't matter)
941 * (DISP_FREEMEM, 0) only used to prevent memory leak during exit)
942 * (x, y) display the glyph at the location
944 * DISP_BEAM - Display the given glyph at each location, but do not erase
945 * any until the close call.
946 * DISP_TETHER - Display a tether glyph at each location, and the tethered
947 * object at the farthest location, but do not erase any
948 * until the return trip or close.
949 * DISP_FLASH - Display the given glyph at each location, but erase the
950 * previous location's glyph.
951 * DISP_ALWAYS - Like DISP_FLASH, but vision is not taken into account.
954 #define TMP_AT_MAX_GLYPHS (COLNO * 2)
956 static struct tmp_glyph {
957 coord saved[TMP_AT_MAX_GLYPHS]; /* previously updated positions */
958 int sidx; /* index of next unused slot in saved[] */
959 int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
960 int glyph; /* glyph to use when printing */
961 struct tmp_glyph *prev;
968 static struct tmp_glyph *tglyph = (struct tmp_glyph *) 0;
969 struct tmp_glyph *tmp;
979 else /* nested effect; we need dynamic memory */
980 tmp = (struct tmp_glyph *) alloc(sizeof *tmp);
986 flush_screen(0); /* flush buffered glyphs */
989 case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
992 if (tglyph != &tgfirst)
993 free((genericptr_t) tglyph);
1003 panic("tmp_at: tglyph not initialized");
1011 if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
1014 /* Erase (reset) from source to end */
1015 for (i = 0; i < tglyph->sidx; i++)
1016 newsym(tglyph->saved[i].x, tglyph->saved[i].y);
1017 } else if (tglyph->style == DISP_TETHER) {
1020 if (y == BACKTRACK && tglyph->sidx > 1) {
1022 for (i = tglyph->sidx - 1; i > 0; i--) {
1023 newsym(tglyph->saved[i].x, tglyph->saved[i].y);
1024 show_glyph(tglyph->saved[i - 1].x,
1025 tglyph->saved[i - 1].y, tglyph->glyph);
1026 flush_screen(0); /* make sure it shows up */
1031 for (i = 0; i < tglyph->sidx; i++)
1032 newsym(tglyph->saved[i].x, tglyph->saved[i].y);
1033 } else { /* DISP_FLASH or DISP_ALWAYS */
1034 if (tglyph->sidx) /* been called at least once */
1035 newsym(tglyph->saved[0].x, tglyph->saved[0].y);
1037 /* tglyph->sidx = 0; -- about to be freed, so not necessary */
1039 if (tglyph != &tgfirst)
1040 free((genericptr_t) tglyph);
1044 default: /* do it */
1047 if (tglyph->style == DISP_BEAM || tglyph->style == DISP_ALL) {
1048 if (tglyph->style != DISP_ALL && !cansee(x, y))
1050 if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
1051 break; /* too many locations */
1052 /* save pos for later erasing */
1053 tglyph->saved[tglyph->sidx].x = x;
1054 tglyph->saved[tglyph->sidx].y = y;
1056 } else if (tglyph->style == DISP_TETHER) {
1057 if (tglyph->sidx >= TMP_AT_MAX_GLYPHS)
1058 break; /* too many locations */
1062 px = tglyph->saved[tglyph->sidx-1].x;
1063 py = tglyph->saved[tglyph->sidx-1].y;
1064 show_glyph(px, py, tether_glyph(px, py));
1066 /* save pos for later use or erasure */
1067 tglyph->saved[tglyph->sidx].x = x;
1068 tglyph->saved[tglyph->sidx].y = y;
1070 } else { /* DISP_FLASH/ALWAYS */
1071 if (tglyph->sidx) { /* not first call, so reset previous pos */
1072 newsym(tglyph->saved[0].x, tglyph->saved[0].y);
1073 tglyph->sidx = 0; /* display is presently up to date */
1075 if (!cansee(x, y) && tglyph->style != DISP_ALWAYS)
1077 tglyph->saved[0].x = x;
1078 tglyph->saved[0].y = y;
1082 show_glyph(x, y, tglyph->glyph); /* show it */
1083 flush_screen(0); /* make sure it shows up */
1089 * flash_glyph_at(x, y, glyph, repeatcount)
1091 * Briefly flash between the passed glyph and the glyph that's
1092 * meant to be at the location.
1095 flash_glyph_at(x, y, tg, rpt)
1101 rpt *= 2; /* two loop iterations per 'count' */
1103 glyph[1] = (level.flags.hero_memory) ? levl[x][y].glyph
1104 : back_to_glyph(x, y);
1105 /* even iteration count (guaranteed) ends with glyph[1] showing;
1106 caller might want to override that, but no newsym() calls here
1107 in case caller has tinkered with location visibility */
1108 for (i = 0; i < rpt; i++) {
1109 show_glyph(x, y, glyph[i % 2]);
1118 * The hero is swallowed. Show a special graphics sequence for this. This
1119 * bypasses all of the display routines and messes with buffered screen
1120 * directly. This method works because both vision and display check for
1127 static xchar lastx, lasty; /* last swallowed position */
1128 int swallower, left_ok, rght_ok;
1136 /* Clear old location */
1137 for (y = lasty - 1; y <= lasty + 1; y++)
1138 for (x = lastx - 1; x <= lastx + 1; x++)
1140 show_glyph(x, y, cmap_to_glyph(S_stone));
1143 swallower = monsndx(u.ustuck->data);
1144 /* assume isok(u.ux,u.uy) */
1145 left_ok = isok(u.ux - 1, u.uy);
1146 rght_ok = isok(u.ux + 1, u.uy);
1148 * Display the hero surrounded by the monster's stomach.
1150 if (isok(u.ux, u.uy - 1)) {
1152 show_glyph(u.ux - 1, u.uy - 1,
1153 swallow_to_glyph(swallower, S_sw_tl));
1154 show_glyph(u.ux, u.uy - 1, swallow_to_glyph(swallower, S_sw_tc));
1156 show_glyph(u.ux + 1, u.uy - 1,
1157 swallow_to_glyph(swallower, S_sw_tr));
1161 show_glyph(u.ux - 1, u.uy, swallow_to_glyph(swallower, S_sw_ml));
1164 show_glyph(u.ux + 1, u.uy, swallow_to_glyph(swallower, S_sw_mr));
1166 if (isok(u.ux, u.uy + 1)) {
1168 show_glyph(u.ux - 1, u.uy + 1,
1169 swallow_to_glyph(swallower, S_sw_bl));
1170 show_glyph(u.ux, u.uy + 1, swallow_to_glyph(swallower, S_sw_bc));
1172 show_glyph(u.ux + 1, u.uy + 1,
1173 swallow_to_glyph(swallower, S_sw_br));
1176 /* Update the swallowed position. */
1184 * Similar to swallowed() in operation. Shows hero when underwater
1185 * except when in water level. Special routines exist for that.
1191 static xchar lastx, lasty;
1192 static boolean dela;
1195 /* swallowing has a higher precedence than under water */
1196 if (Is_waterlevel(&u.uz) || u.uswallow)
1200 if (mode == 1 || dela) {
1204 /* delayed full update */
1205 } else if (mode == 2) {
1209 /* limited update */
1211 for (y = lasty - 1; y <= lasty + 1; y++)
1212 for (x = lastx - 1; x <= lastx + 1; x++)
1214 show_glyph(x, y, cmap_to_glyph(S_stone));
1218 * TODO? Should this honor Xray radius rather than force radius 1?
1221 for (x = u.ux - 1; x <= u.ux + 1; x++)
1222 for (y = u.uy - 1; y <= u.uy + 1; y++)
1223 if (isok(x, y) && (is_pool_or_lava(x, y) || is_ice(x, y))) {
1224 if (Blind && !(x == u.ux && y == u.uy))
1225 show_glyph(x, y, cmap_to_glyph(S_stone));
1236 * Very restricted display. You can only see yourself.
1242 static boolean dela;
1244 /* swallowing has a higher precedence than under ground */
1249 if (mode == 1 || dela) {
1253 /* delayed full update */
1254 } else if (mode == 2) {
1258 /* limited update */
1264 /* ======================================================================== */
1267 * Loop through all of the monsters and update them. Called when:
1268 * + going blind & telepathic
1269 * + regaining sight & telepathic
1270 * + getting and losing infravision
1272 * + doing a full screen redraw
1273 * + see invisible times out or a ring of see invisible is taken off
1274 * + when a potion of see invisible is quaffed or a ring of see
1275 * invisible is put on
1276 * + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1277 * + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1283 register struct monst *mon;
1284 int new_warn_obj_cnt = 0;
1286 if (defer_see_monsters)
1289 for (mon = fmon; mon; mon = mon->nmon) {
1290 if (DEADMONSTER(mon))
1292 newsym(mon->mx, mon->my);
1295 if (Warn_of_mon && (context.warntype.obj & mon->data->mflags2) != 0L)
1299 * Make Sting glow blue or stop glowing if required.
1301 if (new_warn_obj_cnt != warn_obj_cnt) {
1302 Sting_effects(new_warn_obj_cnt);
1303 warn_obj_cnt = new_warn_obj_cnt;
1306 /* when mounted, hero's location gets caught by monster loop */
1312 * Block/unblock light depending on what a mimic is mimicing and if it's
1313 * invisible or not. Should be called only when the state of See_invisible
1317 set_mimic_blocking()
1319 register struct monst *mon;
1321 for (mon = fmon; mon; mon = mon->nmon) {
1322 if (DEADMONSTER(mon))
1324 if (mon->minvis && is_lightblocker_mappear(mon)) {
1326 block_point(mon->mx, mon->my);
1328 unblock_point(mon->mx, mon->my);
1334 * Loop through all of the object *locations* and update them. Called when
1340 register struct obj *obj;
1341 for (obj = fobj; obj; obj = obj->nobj)
1342 if (vobj_at(obj->ox, obj->oy) == obj)
1343 newsym(obj->ox, obj->oy);
1347 * Update hallucinated traps.
1355 for (trap = ftrap; trap; trap = trap->ntrap) {
1356 glyph = glyph_at(trap->tx, trap->ty);
1357 if (glyph_is_trap(glyph))
1358 newsym(trap->tx, trap->ty);
1363 * Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1368 flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1382 register struct rm *lev;
1385 return; /* display isn't ready yet */
1391 if (Underwater && !Is_waterlevel(&u.uz)) {
1400 /* shut down vision */
1404 * This routine assumes that cls() does the following:
1405 * + fills the physical screen with the symbol for rock
1406 * + clears the glyph buffer
1410 /* display memory */
1411 for (x = 1; x < COLNO; x++) {
1413 for (y = 0; y < ROWNO; y++, lev++)
1414 if (lev->glyph != cmap_to_glyph(S_stone))
1415 show_glyph(x, y, lev->glyph);
1418 /* see what is to be seen */
1421 /* overlay with monsters */
1429 context.botlx = 1; /* force a redraw of the bottom line */
1432 /* for panning beyond a clipped region; resend the current map data to
1433 the interface rather than use docrt()'s regeneration of that data */
1440 * Not sure whether this is actually necessary; save and restore did
1441 * used to get much too involved with each dungeon level as it was
1444 * !u.ux: display isn't ready yet; (restoring || !on_level()): was part
1445 * of cliparound() but interface shouldn't access this much internals
1447 if (!u.ux || restoring || !on_level(&u.uz0, &u.uz))
1451 * This yields sensible clipping when #terrain+getpos is in
1452 * progress and the screen displays something other than what
1453 * the map would currently be showing.
1455 for (y = 0; y < ROWNO; ++y)
1456 for (x = 1; x < COLNO; ++x) {
1457 glyph = glyph_at(x, y); /* not levl[x][y].glyph */
1458 print_glyph(WIN_MAP, x, y, glyph, get_bk_glyph(x, y));
1463 /* ======================================================================== */
1464 /* Glyph Buffering (3rd screen) =========================================== */
1467 xchar new; /* perhaps move this bit into the rm structure. */
1471 static gbuf_entry gbuf[ROWNO][COLNO];
1472 static char gbuf_start[ROWNO];
1473 static char gbuf_stop[ROWNO];
1475 /* FIXME: This is a dirty hack, because newsym() doesn't distinguish
1476 * between object piles and single objects, it doesn't mark the location
1484 if (gbuf_start[y] > x)
1486 if (gbuf_stop[y] < x)
1491 * Store the glyph in the 3rd screen for later flushing.
1494 show_glyph(x, y, glyph)
1498 * Check for bad positions and glyphs.
1504 /* column 0 is invalid, but it's often used as a flag, so ignore it */
1509 * This assumes an ordering of the offsets. See display.h for
1513 if (glyph >= GLYPH_WARNING_OFF
1514 && glyph < GLYPH_STATUE_OFF) { /* a warning */
1516 offset = glyph - GLYPH_WARNING_OFF;
1517 } else if (glyph >= GLYPH_SWALLOW_OFF) { /* swallow border */
1518 text = "swallow border";
1519 offset = glyph - GLYPH_SWALLOW_OFF;
1520 } else if (glyph >= GLYPH_ZAP_OFF) { /* zap beam */
1522 offset = glyph - GLYPH_ZAP_OFF;
1523 } else if (glyph >= GLYPH_EXPLODE_OFF) { /* explosion */
1525 offset = glyph - GLYPH_EXPLODE_OFF;
1526 } else if (glyph >= GLYPH_CMAP_OFF) { /* cmap */
1527 text = "cmap_index";
1528 offset = glyph - GLYPH_CMAP_OFF;
1529 } else if (glyph >= GLYPH_OBJ_OFF) { /* object */
1531 offset = glyph - GLYPH_OBJ_OFF;
1532 } else if (glyph >= GLYPH_RIDDEN_OFF) { /* ridden mon */
1533 text = "ridden mon";
1534 offset = glyph - GLYPH_RIDDEN_OFF;
1535 } else if (glyph >= GLYPH_BODY_OFF) { /* a corpse */
1537 offset = glyph - GLYPH_BODY_OFF;
1538 } else if (glyph >= GLYPH_DETECT_OFF) { /* detected mon */
1539 text = "detected mon";
1540 offset = glyph - GLYPH_DETECT_OFF;
1541 } else if (glyph >= GLYPH_INVIS_OFF) { /* invisible mon */
1542 text = "invisible mon";
1543 offset = glyph - GLYPH_INVIS_OFF;
1544 } else if (glyph >= GLYPH_PET_OFF) { /* a pet */
1546 offset = glyph - GLYPH_PET_OFF;
1547 } else { /* a monster */
1552 impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].", x, y,
1553 glyph, text, offset);
1557 if (glyph >= MAX_GLYPH) {
1558 impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).", glyph,
1563 if (gbuf[y][x].glyph != glyph || iflags.use_background_glyph) {
1564 gbuf[y][x].glyph = glyph;
1566 if (gbuf_start[y] > x)
1568 if (gbuf_stop[y] < x)
1574 * Reset the changed glyph borders so that none of the 3rd screen has
1577 #define reset_glyph_bbox() \
1581 for (i = 0; i < ROWNO; i++) { \
1582 gbuf_start[i] = COLNO - 1; \
1587 static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1589 * Turn the 3rd screen into stone.
1592 clear_glyph_buffer()
1595 register gbuf_entry *gptr;
1597 for (y = 0; y < ROWNO; y++) {
1599 for (x = COLNO; x; x--) {
1607 * Assumes that the indicated positions are filled with S_stone glyphs.
1610 row_refresh(start, stop, y)
1615 for (x = start; x <= stop; x++)
1616 if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
1617 print_glyph(WIN_MAP, x, y, gbuf[y][x].glyph, get_bk_glyph(x, y));
1623 static boolean in_cls = 0;
1628 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1629 context.botlx = 1; /* force update of botl window */
1630 clear_nhwindow(WIN_MAP); /* clear physical screen */
1632 clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1637 * Synch the third screen with the display.
1640 flush_screen(cursor_on_u)
1643 /* Prevent infinite loops on errors:
1644 * flush_screen->print_glyph->impossible->pline->flush_screen
1646 static int flushing = 0;
1647 static int delay_flushing = 0;
1650 if (cursor_on_u == -1)
1651 delay_flushing = !delay_flushing;
1655 return; /* if already flushing then return */
1657 #ifdef HANGUPHANDLING
1658 if (program_state.done_hup)
1662 for (y = 0; y < ROWNO; y++) {
1663 register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
1665 for (; x <= gbuf_stop[y]; gptr++, x++)
1667 print_glyph(WIN_MAP, x, y, gptr->glyph, get_bk_glyph(x, y));
1673 curs(WIN_MAP, u.ux, u.uy); /* move cursor to the hero */
1674 display_nhwindow(WIN_MAP, FALSE);
1677 if (context.botl || context.botlx)
1679 else if (iflags.time_botl)
1683 /* ======================================================================== */
1688 * Use the information in the rm structure at the given position to create
1689 * a glyph of a background.
1691 * I had to add a field in the rm structure (horizontal) so that we knew
1692 * if open doors and secret doors were horizontal or vertical. Previously,
1693 * the screen symbol had the horizontal/vertical information set at
1694 * level generation time.
1696 * I used the 'ladder' field (really doormask) for deciding if stairwells
1697 * were up or down. I didn't want to check the upstairs and dnstairs
1705 struct rm *ptr = &(levl[x][y]);
1710 idx = level.flags.arboreal ? S_tree : S_stone;
1716 idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1730 idx = ptr->seenv ? wall_angle(ptr) : S_stone;
1733 if (ptr->doormask) {
1734 if (ptr->doormask & D_BROKEN)
1736 else if (ptr->doormask & D_ISOPEN)
1737 idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
1738 else /* else is closed */
1739 idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
1754 idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
1757 idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
1790 idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
1793 switch (ptr->drawbridgemask & DB_UNDER) {
1807 impossible("Strange db-under: %d",
1808 ptr->drawbridgemask & DB_UNDER);
1809 idx = S_room; /* something is better than nothing */
1813 case DRAWBRIDGE_DOWN:
1814 idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
1817 impossible("back_to_glyph: unknown level type [ = %d ]", ptr->typ);
1822 return cmap_to_glyph(idx);
1826 * swallow_to_glyph()
1828 * Convert a monster number and a swallow location into the correct glyph.
1829 * If you don't want a patchwork monster while hallucinating, decide on
1830 * a random monster in swallowed() and don't use what_mon() here.
1833 swallow_to_glyph(mnum, loc)
1837 if (loc < S_sw_tl || S_sw_br < loc) {
1838 impossible("swallow_to_glyph: bad swallow location");
1841 return ((int) (what_mon(mnum, rn2_on_display_rng) << 3) |
1842 (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
1848 * Change the given zap direction and beam type into a glyph. Each beam
1849 * type has four glyphs, one for each of the symbols below. The order of
1850 * the zap symbols [0-3] as defined in rm.h are:
1852 * | S_vbeam ( 0, 1) or ( 0,-1)
1853 * - S_hbeam ( 1, 0) or (-1, 0)
1854 * \ S_lslant ( 1, 1) or (-1,-1)
1855 * / S_rslant (-1, 1) or ( 1,-1)
1858 zapdir_to_glyph(dx, dy, beam_type)
1859 register int dx, dy;
1862 if (beam_type >= NUM_ZAP) {
1863 impossible("zapdir_to_glyph: illegal beam type");
1866 dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
1868 return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
1872 * Utility routine for dowhatis() used to find out the glyph displayed at
1873 * the location. This isn't necessarily the same as the glyph in the levl
1874 * structure, so we must check the "third screen".
1880 if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
1881 return cmap_to_glyph(S_room); /* XXX */
1882 return gbuf[y][x].glyph;
1886 * This will be used to get the glyph for the background so that
1887 * it can potentially be merged into graphical window ports to
1888 * improve the appearance of stuff on dark room squares and the
1891 * Until that is working correctly in the branch, however, for now
1892 * we just return NO_GLYPH as an indicator to ignore it.
1894 * [This should be using background as recorded for #overview rather
1895 * than current data from the map.]
1902 int idx, bkglyph = NO_GLYPH;
1903 struct rm *lev = &levl[x][y];
1905 if (iflags.use_background_glyph && lev->seenv != 0
1906 && gbuf[y][x].glyph != cmap_to_glyph(S_stone)) {
1910 idx = level.flags.arboreal ? S_tree : S_stone;
1916 idx = (lev->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1942 if (!cansee(x, y) && (!lev->waslit || flags.dark_room)) {
1943 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
1944 if (lev->typ == CORR && idx == S_litcorr)
1946 else if (idx == S_room)
1947 idx = (flags.dark_room && iflags.use_color)
1948 ? DARKROOMSYM : S_stone;
1952 bkglyph = cmap_to_glyph(idx);
1957 /* ------------------------------------------------------------------------ */
1958 /* Wall Angle ------------------------------------------------------------- */
1962 static const char *FDECL(type_to_name, (int));
1963 static void FDECL(error4, (int, int, int, int, int, int));
1965 static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1966 static const char *type_names[MAX_TYPE] = {
1967 "STONE", "VWALL", "HWALL", "TLCORNER", "TRCORNER", "BLCORNER", "BRCORNER",
1968 "CROSSWALL", "TUWALL", "TDWALL", "TLWALL", "TRWALL", "DBWALL", "TREE",
1969 "SDOOR", "SCORR", "POOL", "MOAT", "WATER", "DRAWBRIDGE_UP", "LAVAPOOL",
1970 "IRON_BARS", "DOOR", "CORR", "ROOM", "STAIRS", "LADDER", "FOUNTAIN",
1971 "THRONE", "SINK", "GRAVE", "ALTAR", "ICE", "DRAWBRIDGE_DOWN", "AIR",
1979 return (type < 0 || type >= MAX_TYPE) ? "unknown" : type_names[type];
1983 error4(x, y, a, b, c, dd)
1984 int x, y, a, b, c, dd;
1986 pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1987 type_to_name(levl[x][y].typ), x, y,
1988 a ? "1" : "", b ? "2" : "", c ? "3" : "", dd ? "4" : "");
1989 bad_count[levl[x][y].typ]++;
1991 #endif /* WA_VERBOSE */
1994 * Return 'which' if position is implies an unfinished exterior. Return
1995 * zero otherwise. Unfinished implies outer area is rock or a corridor.
1997 * Things that are ambiguous: lava
2000 check_pos(x, y, which)
2007 type = levl[x][y].typ;
2008 if (IS_ROCK(type) || type == CORR || type == SCORR)
2013 /* Return TRUE if more than one is non-zero. */
2017 more_than_one(x, y, a, b, c)
2020 if ((a && (b | c)) || (b && (a | c)) || (c && (a | b))) {
2021 error4(x, y, a, b, c, 0);
2027 #define more_than_one(x, y, a, b, c) \
2028 (((a) && ((b) | (c))) || ((b) && ((a) | (c))) || ((c) && ((a) | (b))))
2031 /* Return the wall mode for a T wall. */
2033 set_twall(x0, y0, x1, y1, x2, y2, x3, y3)
2034 int x0, y0; /* used #if WA_VERBOSE */
2035 int x1, y1, x2, y2, x3, y3;
2037 int wmode, is_1, is_2, is_3;
2040 /* non-verbose more_than_one() doesn't use these */
2045 is_1 = check_pos(x1, y1, WM_T_LONG);
2046 is_2 = check_pos(x2, y2, WM_T_BL);
2047 is_3 = check_pos(x3, y3, WM_T_BR);
2048 if (more_than_one(x0, y0, is_1, is_2, is_3)) {
2051 wmode = is_1 + is_2 + is_3;
2056 /* Return wall mode for a horizontal or vertical wall. */
2058 set_wall(x, y, horiz)
2061 int wmode, is_1, is_2;
2064 is_1 = check_pos(x, y - 1, WM_W_TOP);
2065 is_2 = check_pos(x, y + 1, WM_W_BOTTOM);
2067 is_1 = check_pos(x - 1, y, WM_W_LEFT);
2068 is_2 = check_pos(x + 1, y, WM_W_RIGHT);
2070 if (more_than_one(x, y, is_1, is_2, 0)) {
2073 wmode = is_1 + is_2;
2078 /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
2080 set_corn(x1, y1, x2, y2, x3, y3, x4, y4)
2081 int x1, y1, x2, y2, x3, y3, x4, y4;
2083 int wmode, is_1, is_2, is_3, is_4;
2085 is_1 = check_pos(x1, y1, 1);
2086 is_2 = check_pos(x2, y2, 1);
2087 is_3 = check_pos(x3, y3, 1);
2088 is_4 = check_pos(x4, y4, 1); /* inner location */
2091 * All 4 should not be true. So if the inner location is rock,
2092 * use it. If all of the outer 3 are true, use outer. We currently
2093 * can't cover the case where only part of the outer is rock, so
2094 * we just say that all the walls are finished (if not overridden
2095 * by the inner section).
2099 } else if (is_1 && is_2 && is_3)
2102 wmode = 0; /* finished walls on all sides */
2107 /* Return mode for a crosswall. */
2112 int wmode, is_1, is_2, is_3, is_4;
2114 is_1 = check_pos(x - 1, y - 1, 1);
2115 is_2 = check_pos(x + 1, y - 1, 1);
2116 is_3 = check_pos(x + 1, y + 1, 1);
2117 is_4 = check_pos(x - 1, y + 1, 1);
2119 wmode = is_1 + is_2 + is_3 + is_4;
2121 if (is_1 && is_3 && (is_2 + is_4 == 0)) {
2123 } else if (is_2 && is_4 && (is_1 + is_3 == 0)) {
2127 error4(x, y, is_1, is_2, is_3, is_4);
2143 /* Called from mklev. Scan the level and set the wall modes. */
2152 for (x = 0; x < MAX_TYPE; x++)
2156 for (x = 0; x < COLNO; x++)
2157 for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
2160 wmode = set_wall(x, y, (int) lev->horizontal);
2163 wmode = set_wall(x, y, 0);
2166 wmode = set_wall(x, y, 1);
2169 wmode = set_twall(x, y, x, y - 1, x - 1, y + 1, x + 1, y + 1);
2172 wmode = set_twall(x, y, x, y + 1, x + 1, y - 1, x - 1, y - 1);
2175 wmode = set_twall(x, y, x + 1, y, x - 1, y - 1, x - 1, y + 1);
2178 wmode = set_twall(x, y, x - 1, y, x + 1, y + 1, x + 1, y - 1);
2182 set_corn(x - 1, y - 1, x, y - 1, x - 1, y, x + 1, y + 1);
2186 set_corn(x, y - 1, x + 1, y - 1, x + 1, y, x - 1, y + 1);
2190 set_corn(x, y + 1, x - 1, y + 1, x - 1, y, x + 1, y - 1);
2194 set_corn(x + 1, y, x + 1, y + 1, x, y + 1, x - 1, y - 1);
2197 wmode = set_crosswall(x, y);
2201 wmode = -1; /* don't set wall info */
2206 lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
2210 /* check if any bad positions found */
2211 for (x = y = 0; x < MAX_TYPE; x++)
2214 y = 1; /* only print once */
2215 pline("set_wall_type: wall mode problems with: ");
2217 pline("%s %d;", type_names[x], bad_count[x]);
2219 #endif /* WA_VERBOSE */
2222 /* ------------------------------------------------------------------------ */
2223 /* This matrix is used here and in vision.c. */
2224 unsigned char seenv_matrix[3][3] = { { SV2, SV1, SV0 },
2225 { SV3, SVALL, SV7 },
2226 { SV4, SV5, SV6 } };
2228 #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
2230 /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
2232 set_seenv(lev, x0, y0, x, y)
2234 int x0, y0, x, y; /* from, to */
2236 int dx = x - x0, dy = y0 - y;
2238 lev->seenv |= seenv_matrix[sign(dy) + 1][sign(dx) + 1];
2241 /* Called by blackout(vault.c) when vault guard removes temporary corridor,
2242 turning spot <x0,y0> back into stone; <x1,y1> is an adjacent spot. */
2244 unset_seenv(lev, x0, y0, x1, y1)
2245 struct rm *lev; /* &levl[x1][y1] */
2246 int x0, y0, x1, y1; /* from, to; abs(x1-x0)==1 && abs(y0-y1)==1 */
2248 int dx = x1 - x0, dy = y0 - y1;
2250 lev->seenv &= ~seenv_matrix[dy + 1][dx + 1];
2253 /* ------------------------------------------------------------------------ */
2255 /* T wall types, one for each row in wall_matrix[][]. */
2262 * These are the column names of wall_matrix[][]. They are the "results"
2263 * of a tdwall pattern match. All T walls are rotated so they become
2264 * a tdwall. Then we do a single pattern match, but return the
2265 * correct result for the original wall by using different rows for
2266 * each of the wall types.
2274 static const int wall_matrix[4][5] = {
2275 { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
2276 { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
2277 { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
2278 { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
2281 /* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][].
2289 * These are the column names for cross_matrix[][]. They express results
2290 * in C_br (bottom right) terms. All crosswalls with a single solid
2291 * quarter are rotated so the solid section is at the bottom right.
2292 * We pattern match on that, but return the correct result depending
2293 * on which row we'ere looking at.
2302 static const int cross_matrix[4][6] = {
2303 { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
2304 { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
2305 { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
2306 { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
2309 /* Print out a T wall warning and all interesting info. */
2314 static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
2317 if (lev->typ == TUWALL)
2319 else if (lev->typ == TLWALL)
2321 else if (lev->typ == TRWALL)
2323 else if (lev->typ == TDWALL)
2327 impossible(warn_str, wname, lev->wall_info & WM_MASK,
2328 (unsigned int) lev->seenv);
2332 * Return the correct graphics character index using wall type, wall mode,
2333 * and the seen vector. It is expected that seenv is non zero.
2335 * All T-wall vectors are rotated to be TDWALL. All single crosswall
2336 * blocks are rotated to bottom right. All double crosswall are rotated
2337 * to W_X_BLTR. All results are converted back.
2339 * The only way to understand this is to take out pen and paper and
2340 * draw diagrams. See rm.h for more details on the wall modes and
2347 register unsigned int seenv = lev->seenv & 0xff;
2351 #define only(sv, bits) (((sv) & (bits)) && !((sv) & ~(bits)))
2354 row = wall_matrix[T_u];
2355 seenv = (seenv >> 4 | seenv << 4) & 0xff; /* rotate to tdwall */
2358 row = wall_matrix[T_l];
2359 seenv = (seenv >> 2 | seenv << 6) & 0xff; /* rotate to tdwall */
2362 row = wall_matrix[T_r];
2363 seenv = (seenv >> 6 | seenv << 2) & 0xff; /* rotate to tdwall */
2366 row = wall_matrix[T_d];
2368 switch (lev->wall_info & WM_MASK) {
2372 } else if (seenv == SV6) {
2374 } else if (seenv & (SV3 | SV5 | SV7)
2375 || ((seenv & SV4) && (seenv & SV6))) {
2377 } else if (seenv & (SV0 | SV1 | SV2)) {
2378 col = (seenv & (SV4 | SV6) ? T_tdwall : T_hwall);
2385 if (seenv & (SV3 | SV4) && !(seenv & (SV5 | SV6 | SV7))) {
2387 } else if (seenv & (SV6 | SV7) && !(seenv & (SV3 | SV4 | SV5))) {
2389 } else if ((seenv & SV5)
2390 || ((seenv & (SV3 | SV4)) && (seenv & (SV6 | SV7)))) {
2393 /* only SV0|SV1|SV2 */
2394 if (!only(seenv, SV0 | SV1 | SV2))
2400 #if 0 /* older method, fixed */
2401 if (only(seenv, SV4 | SV5)) {
2403 } else if ((seenv & (SV0 | SV1 | SV2))
2404 && only(seenv, SV0 | SV1 | SV2 | SV6 | SV7)) {
2406 } else if ((seenv & SV3)
2407 || ((seenv & (SV0 | SV1 | SV2))
2408 && (seenv & (SV4 | SV5)))) {
2416 if (only(seenv, SV4 | SV5))
2418 else if ((seenv & (SV0 | SV1 | SV2 | SV7))
2419 && !(seenv & (SV3 | SV4 | SV5)))
2421 else if (only(seenv, SV6))
2427 #if 0 /* older method, fixed */
2428 if (only(seenv, SV5 | SV6)) {
2430 } else if ((seenv & (SV0 | SV1 | SV2))
2431 && only(seenv, SV0 | SV1 | SV2 | SV3 | SV4)) {
2433 } else if ((seenv & SV7)
2434 || ((seenv & (SV0 | SV1 | SV2))
2435 && (seenv & (SV5 | SV6)))) {
2443 if (only(seenv, SV5 | SV6))
2445 else if ((seenv & (SV0 | SV1 | SV2 | SV3))
2446 && !(seenv & (SV5 | SV6 | SV7)))
2448 else if (only(seenv, SV4))
2455 impossible("wall_angle: unknown T wall mode %d",
2456 lev->wall_info & WM_MASK);
2464 if (lev->horizontal)
2468 switch (lev->wall_info & WM_MASK) {
2470 idx = seenv ? S_vwall : S_stone;
2473 idx = seenv & (SV1 | SV2 | SV3 | SV4 | SV5) ? S_vwall : S_stone;
2476 idx = seenv & (SV0 | SV1 | SV5 | SV6 | SV7) ? S_vwall : S_stone;
2479 impossible("wall_angle: unknown vwall mode %d",
2480 lev->wall_info & WM_MASK);
2488 switch (lev->wall_info & WM_MASK) {
2490 idx = seenv ? S_hwall : S_stone;
2493 idx = seenv & (SV3 | SV4 | SV5 | SV6 | SV7) ? S_hwall : S_stone;
2496 idx = seenv & (SV0 | SV1 | SV2 | SV3 | SV7) ? S_hwall : S_stone;
2499 impossible("wall_angle: unknown hwall mode %d",
2500 lev->wall_info & WM_MASK);
2506 #define set_corner(idx, lev, which, outer, inner, name) \
2507 switch ((lev)->wall_info & WM_MASK) { \
2512 idx = seenv & (outer) ? which : S_stone; \
2515 idx = seenv & ~(inner) ? which : S_stone; \
2518 impossible("wall_angle: unknown %s mode %d", name, \
2519 (lev)->wall_info &WM_MASK); \
2525 set_corner(idx, lev, S_tlcorn, (SV3 | SV4 | SV5), SV4, "tlcorn");
2528 set_corner(idx, lev, S_trcorn, (SV5 | SV6 | SV7), SV6, "trcorn");
2531 set_corner(idx, lev, S_blcorn, (SV1 | SV2 | SV3), SV2, "blcorn");
2534 set_corner(idx, lev, S_brcorn, (SV7 | SV0 | SV1), SV0, "brcorn");
2538 switch (lev->wall_info & WM_MASK) {
2542 else if (seenv == SV2)
2544 else if (seenv == SV4)
2546 else if (seenv == SV6)
2548 else if (!(seenv & ~(SV0 | SV1 | SV2))
2549 && (seenv & SV1 || seenv == (SV0 | SV2)))
2551 else if (!(seenv & ~(SV2 | SV3 | SV4))
2552 && (seenv & SV3 || seenv == (SV2 | SV4)))
2554 else if (!(seenv & ~(SV4 | SV5 | SV6))
2555 && (seenv & SV5 || seenv == (SV4 | SV6)))
2557 else if (!(seenv & ~(SV0 | SV6 | SV7))
2558 && (seenv & SV7 || seenv == (SV0 | SV6)))
2565 row = cross_matrix[C_tl];
2566 seenv = (seenv >> 4 | seenv << 4) & 0xff;
2569 row = cross_matrix[C_tr];
2570 seenv = (seenv >> 6 | seenv << 2) & 0xff;
2573 row = cross_matrix[C_bl];
2574 seenv = (seenv >> 2 | seenv << 6) & 0xff;
2577 row = cross_matrix[C_br];
2582 seenv = seenv & ~SV4; /* strip SV4 */
2585 } else if (seenv & (SV2 | SV3)) {
2586 if (seenv & (SV5 | SV6 | SV7))
2588 else if (seenv & (SV0 | SV1))
2592 } else if (seenv & (SV5 | SV6)) {
2593 if (seenv & (SV1 | SV2 | SV3))
2595 else if (seenv & (SV0 | SV7))
2599 } else if (seenv & SV1) {
2600 col = seenv & SV7 ? C_crwall : C_tuwall;
2601 } else if (seenv & SV7) {
2602 col = seenv & SV1 ? C_crwall : C_tlwall;
2604 impossible("wall_angle: bottom of crwall check");
2613 if (only(seenv, SV1 | SV2 | SV3))
2615 else if (only(seenv, SV5 | SV6 | SV7))
2617 else if (only(seenv, SV0 | SV4))
2624 if (only(seenv, SV0 | SV1 | SV7))
2626 else if (only(seenv, SV3 | SV4 | SV5))
2628 else if (only(seenv, SV2 | SV6))
2635 impossible("wall_angle: unknown crosswall mode");
2642 impossible("wall_angle: unexpected wall type %d", lev->typ);