1 /* NetHack 3.6 display.c $NHDT-Date: 1461979957 2016/04/30 01:32:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.83 $ */
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 (telepathy)
45 * Here is a list of the major routines in this file to be used externally:
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.
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.
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().
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.
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
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
113 * flags - Additional information for the typ field. Different for
115 * horizontal - Indicates whether the wall or door is horizontal or
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 */
130 STATIC_DCL boolean
FDECL(more_than_one
, (int, int, int, int, int));
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.
150 magic_map_background(x
, y
, 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
)
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
)
173 show_glyph(x
, y
, glyph
);
175 remember_topology(x
, y
);
179 * The routines map_background(), map_object(), and map_trap() could just
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
189 /* FIXME: some of these use xchars for x and y, and some use ints. Make
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.
200 map_background(x
, y
, show
)
204 register int glyph
= back_to_glyph(x
, y
);
206 if (level
.flags
.hero_memory
)
207 levl
[x
][y
].glyph
= glyph
;
209 show_glyph(x
, y
, glyph
);
215 * Map the trap and print it out if directed. This routine assumes that the
216 * hero can physically see the location.
220 register struct trap
*trap
;
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
;
229 show_glyph(x
, y
, glyph
);
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.
239 map_object(obj
, show
)
240 register struct obj
*obj
;
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();
253 levl
[x
][y
].glyph
= glyph
;
257 show_glyph(x
, y
, glyph
);
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.
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
);
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
293 register struct trap
*trap
;
295 if (!level
.flags
.hero_memory
)
298 if ((trap
= t_at(x
, y
)) != 0 && trap
->tseen
&& !covers_traps(x
, y
))
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
)
308 lev
->glyph
= cmap_to_glyph(S_stone
);
310 levl
[x
][y
].glyph
= cmap_to_glyph(S_stone
); /* default val */
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); \
331 map_background(x, y, show); \
333 remember_topology(x, y); \
337 map_location(x
, y
, show
)
340 _map_location(x
, y
, show
);
344 #define PHYSICALLY_SEEN 1
345 #define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my)))
350 * Note that this is *not* a map_XXXX() function! Monsters sort of float
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
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
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
) {
379 impossible("display_monster: bad m_ap_type value [ = %d ]",
380 (int) mon
->m_ap_type
);
382 show_glyph(x
, y
, mon_to_glyph(mon
));
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
395 int sym
= mon
->mappearance
, glyph
= cmap_to_glyph(sym
);
397 levl
[x
][y
].glyph
= glyph
;
399 show_glyph(x
, y
, glyph
);
400 /* override real topology with mimic's fake one */
401 lastseentyp
[x
][y
] = cmap_to_type(sym
);
407 /* Make a fake object to send to map_object(). */
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
);
422 monnum_to_glyph(what_mon((int) mon
->mappearance
)));
427 /* If the mimic is unsuccessfully mimicing something, display the monster
429 if (!mon_mimic
|| sensed
) {
432 /* [ALI] Only use detected glyphs when monster wouldn't be
433 * visible by any other means.
435 if (sightflags
== DETECTED
&& !mon
->mtame
) {
437 num
= detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL
));
439 num
= detected_mon_to_glyph(mon
);
440 } else if (mon
->mtame
&& !Hallucination
) {
442 num
= petnum_to_glyph(PM_LONG_WORM_TAIL
);
444 num
= pet_to_glyph(mon
);
447 num
= monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL
));
449 num
= mon_to_glyph(mon
);
451 show_glyph(x
, y
, num
);
458 * This is also *not* a map_XXXX() function! Monster warnings float
459 * above everything just like monsters do, but only if the monster
462 * Do not call for worm tails.
466 register struct monst
*mon
;
468 int x
= mon
->mx
, y
= mon
->my
;
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
);
477 impossible("display_warning did not match warning type?");
480 show_glyph(x
, y
, glyph
);
488 if (mon_warning(mon
)) {
489 tmp
= (int) (mon
->m_lev
/ 4); /* match display.h */
490 wl
= (tmp
> WARNCOUNT
- 1) ? WARNCOUNT
- 1 : tmp
;
499 * When hero knows what happened to location, even when blind.
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.
528 register struct monst
*mon
;
533 /* If the hero's memory of an invisible monster is accurate, we want to
535 * him from detecting the same monster over and over again on each turn.
536 * We must return (so we don't erase the monster). (We must also, in the
537 * search function, be sure to skip over previously detected 'I's.)
539 if (glyph_is_invisible(lev
->glyph
) && m_at(x
, y
))
542 /* The hero can't feel non pool locations while under water. */
543 if (Underwater
&& !Is_waterlevel(&u
.uz
) && !is_pool(x
, y
))
546 /* Set the seen vector as if the hero had seen it.
547 It doesn't matter if the hero is levitating or not. */
548 set_seenv(lev
, u
.ux
, u
.uy
, x
, y
);
550 if (!can_reach_floor(FALSE
)) {
552 * Levitation Rules. It is assumed that the hero can feel the state
553 * of the walls around herself and can tell if she is in a corridor,
554 * room, or doorway. Boulders are felt because they are large enough.
555 * Anything else is unknown because the hero can't reach the ground.
556 * This makes things difficult.
558 * Check (and display) in order:
560 * + Stone, walls, and closed doors.
561 * + Boulders. [see a boulder before a doorway]
563 * + Room/water positions
564 * + Everything else (hallways!)
566 if (IS_ROCK(lev
->typ
)
567 || (IS_DOOR(lev
->typ
)
568 && (lev
->doormask
& (D_LOCKED
| D_CLOSED
)))) {
569 map_background(x
, y
, 1);
570 } else if ((boulder
= sobj_at(BOULDER
, x
, y
)) != 0) {
571 map_object(boulder
, 1);
572 } else if (IS_DOOR(lev
->typ
)) {
573 map_background(x
, y
, 1);
574 } else if (IS_ROOM(lev
->typ
) || IS_POOL(lev
->typ
)) {
575 boolean do_room_glyph
;
578 * An open room or water location. Normally we wouldn't touch
579 * this, but we have to get rid of remembered boulder symbols.
580 * This will only occur in rare occasions when the hero goes
581 * blind and doesn't find a boulder where expected (something
582 * came along and picked it up). We know that there is not a
583 * boulder at this location. Show fountains, pools, etc.
584 * underneath if already seen. Otherwise, show the appropriate
587 * Similarly, if the hero digs a hole in a wall or feels a
589 * that used to contain an unseen monster. In these cases,
590 * there's no reason to assume anything was underneath, so
591 * just show the appropriate floor symbol. If something was
592 * embedded in the wall, the glyph will probably already
593 * reflect that. Don't change the symbol in this case.
595 * This isn't quite correct. If the boulder was on top of some
596 * other objects they should be seen once the boulder is removed.
597 * However, we have no way of knowing that what is there now
598 * was there then. So we let the hero have a lapse of memory.
599 * We could also just display what is currently on the top of the
600 * object stack (if anything).
602 do_room_glyph
= FALSE
;
603 if (lev
->glyph
== objnum_to_glyph(BOULDER
)
604 || glyph_is_invisible(lev
->glyph
)) {
605 if (lev
->typ
!= ROOM
&& lev
->seenv
)
606 map_background(x
, y
, 1);
608 do_room_glyph
= TRUE
;
609 } else if (lev
->glyph
>= cmap_to_glyph(S_stone
)
610 && lev
->glyph
< cmap_to_glyph(S_darkroom
)) {
611 do_room_glyph
= TRUE
;
614 lev
->glyph
= (flags
.dark_room
&& iflags
.use_color
615 && !Is_rogue_level(&u
.uz
))
616 ? cmap_to_glyph(S_darkroom
)
617 : (lev
->waslit
? cmap_to_glyph(S_room
)
618 : cmap_to_glyph(S_stone
));
619 show_glyph(x
, y
, lev
->glyph
);
622 /* We feel it (I think hallways are the only things left). */
623 map_background(x
, y
, 1);
624 /* Corridors are never felt as lit (unless remembered that way) */
625 /* (lit_corridor only). */
626 if (lev
->typ
== CORR
&& lev
->glyph
== cmap_to_glyph(S_litcorr
)
628 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
629 else if (lev
->typ
== ROOM
&& flags
.dark_room
&& iflags
.use_color
630 && lev
->glyph
== cmap_to_glyph(S_room
))
631 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_darkroom
));
634 _map_location(x
, y
, 1);
638 * A ball or chain is only felt if it is first on the object
639 * location list. Otherwise, we need to clear the felt bit ---
640 * something has been dropped on the ball/chain. If the bit is
641 * not cleared, then when the ball/chain is moved it will drop
644 if (uchain
->ox
== x
&& uchain
->oy
== y
) {
645 if (level
.objects
[x
][y
] == uchain
)
646 u
.bc_felt
|= BC_CHAIN
;
648 u
.bc_felt
&= ~BC_CHAIN
; /* do not feel the chain */
650 if (!carried(uball
) && uball
->ox
== x
&& uball
->oy
== y
) {
651 if (level
.objects
[x
][y
] == uball
)
652 u
.bc_felt
|= BC_BALL
;
654 u
.bc_felt
&= ~BC_BALL
; /* do not feel the ball */
658 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
659 if (lev
->typ
== ROOM
&& lev
->glyph
== cmap_to_glyph(S_room
)
660 && (!lev
->waslit
|| (flags
.dark_room
&& iflags
.use_color
)))
661 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(
662 flags
.dark_room
? S_darkroom
: S_stone
));
663 else if (lev
->typ
== CORR
&& lev
->glyph
== cmap_to_glyph(S_litcorr
)
665 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
667 /* draw monster on top if we can sense it */
668 if ((x
!= u
.ux
|| y
!= u
.uy
) && (mon
= m_at(x
, y
)) && sensemon(mon
))
669 display_monster(x
, y
, mon
,
670 (tp_sensemon(mon
) || MATCH_WARN_OF_MON(mon
))
679 * Possibly put a new glyph at the given location.
685 register struct monst
*mon
;
686 register struct rm
*lev
= &(levl
[x
][y
]);
688 register xchar worm_tail
;
692 #ifdef HANGUPHANDLING
693 if (program_state
.done_hup
)
697 /* only permit updating the hero when swallowed */
699 if (x
== u
.ux
&& y
== u
.uy
)
703 if (Underwater
&& !Is_waterlevel(&u
.uz
)) {
704 /* don't do anything unless (x,y) is an adjacent underwater position
715 if (dx
> 1 || dy
> 1)
719 /* Can physically see the location. */
721 NhRegion
*reg
= visible_region_at(x
, y
);
723 * Don't use templit here: E.g.
725 * lev->waslit = !!(lev->lit || templit(x,y));
727 * Otherwise we have the "light pool" problem, where non-permanently
728 * lit areas just out of sight stay remembered as lit. They should
731 * Perhaps ALL areas should revert to their "unlit" look when
734 lev
->waslit
= (lev
->lit
!= 0); /* remember lit condition */
736 /* normal region shown only on accessible positions, but poison clouds
737 * also shown above lava, pools and moats.
739 if (reg
!= NULL
&& (ACCESSIBLE(lev
->typ
)
740 || (reg
->glyph
== cmap_to_glyph(S_poisoncloud
)
741 && (lev
->typ
== LAVAPOOL
|| lev
->typ
== POOL
742 || lev
->typ
== MOAT
)))) {
743 show_region(reg
, x
, y
);
746 if (x
== u
.ux
&& y
== u
.uy
) {
748 _map_location(x
, y
, 0); /* map *under* self */
751 /* we can see what is there */
752 _map_location(x
, y
, 1);
755 worm_tail
= is_worm_tail(mon
);
757 mon
&& (worm_tail
? (!mon
->minvis
|| See_invisible
)
758 : (mon_visible(mon
)) || tp_sensemon(mon
)
759 || MATCH_WARN_OF_MON(mon
));
760 if (mon
&& (see_it
|| (!worm_tail
&& Detect_monsters
))) {
762 struct trap
*trap
= t_at(x
, y
);
763 int tt
= trap
? trap
->ttyp
: NO_TRAP
;
765 /* if monster is in a physical trap, you see the trap too
767 if (tt
== BEAR_TRAP
|| tt
== PIT
|| tt
== SPIKED_PIT
772 _map_location(x
, y
, 0); /* map under the monster */
773 /* also gets rid of any invisibility glyph */
774 display_monster(x
, y
, mon
,
775 see_it
? PHYSICALLY_SEEN
: DETECTED
,
777 } else if (mon
&& mon_warning(mon
) && !is_worm_tail(mon
)) {
778 display_warning(mon
);
779 } else if (glyph_is_invisible(levl
[x
][y
].glyph
)) {
782 _map_location(x
, y
, 1); /* map the location */\
785 /* Can't see the location. */
787 if (x
== u
.ux
&& y
== u
.uy
) {
788 feel_location(u
.ux
, u
.uy
); /* forces an update */
792 } else if ((mon
= m_at(x
, y
))
793 && ((see_it
= (tp_sensemon(mon
) || MATCH_WARN_OF_MON(mon
)
794 || (see_with_infrared(mon
)
795 && mon_visible(mon
))))
796 || Detect_monsters
)) {
797 /* Monsters are printed every time. */
798 /* This also gets rid of any invisibility glyph */
799 display_monster(x
, y
, mon
, see_it
? 0 : DETECTED
,
800 is_worm_tail(mon
) ? TRUE
: FALSE
);
801 } else if ((mon
= m_at(x
, y
)) && mon_warning(mon
)
802 && !is_worm_tail(mon
)) {
803 display_warning(mon
);
807 * If the location is remembered as being both dark (waslit is false)
808 * and lit (glyph is a lit room or lit corridor) then it was either:
810 * (1) A dark location that the hero could see through night
813 * (2) Darkened while out of the hero's sight. This can happen
814 * when cursed scroll of light is read.
816 * In either case, we have to manually correct the hero's memory to
817 * match waslit. Deciding when to change waslit is non-trivial.
819 * Note: If flags.lit_corridor is set, then corridors act like room
820 * squares. That is, they light up if in night vision range.
821 * If flags.lit_corridor is not set, then corridors will
822 * remain dark unless lit by a light spell and may darken
823 * again, as discussed above.
825 * These checks and changes must be here and not in back_to_glyph().
826 * They are dependent on the position being out of sight.
828 else if (Is_rogue_level(&u
.uz
)) {
829 if (lev
->glyph
== cmap_to_glyph(S_litcorr
) && lev
->typ
== CORR
)
830 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
831 else if (lev
->glyph
== cmap_to_glyph(S_room
) && lev
->typ
== ROOM
833 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_stone
));
837 else if (!lev
->waslit
|| (flags
.dark_room
&& iflags
.use_color
)) {
838 if (lev
->glyph
== cmap_to_glyph(S_litcorr
) && lev
->typ
== CORR
)
839 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
840 else if (lev
->glyph
== cmap_to_glyph(S_room
) && lev
->typ
== ROOM
)
841 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(DARKROOMSYM
));
846 show_glyph(x
, y
, lev
->glyph
);
856 * Put magic shield pyrotechnics at the given location. This *could* be
857 * pulled into a platform dependent routine for fancier graphics if desired.
867 if (cansee(x
, y
)) { /* Don't see anything if can't see the location */
868 for (i
= 0; i
< SHIELD_COUNT
; i
++) {
869 show_glyph(x
, y
, cmap_to_glyph(shield_static
[i
]));
870 flush_screen(1); /* make sure the glyph shows up */
873 newsym(x
, y
); /* restore the old information */
880 * Temporarily place glyphs on the screen. Do not call delay_output(). It
881 * is up to the caller to decide if it wants to wait [presently, everyone
882 * but explode() wants to delay].
885 * (DISP_BEAM, glyph) open, initialize glyph
886 * (DISP_FLASH, glyph) open, initialize glyph
887 * (DISP_ALWAYS, glyph) open, initialize glyph
888 * (DISP_CHANGE, glyph) change glyph
889 * (DISP_END, 0) close & clean up (second argument doesn't
891 * (DISP_FREEMEM, 0) only used to prevent memory leak during
893 * (x, y) display the glyph at the location
895 * DISP_BEAM - Display the given glyph at each location, but do not erase
896 * any until the close call.
897 * DISP_FLASH - Display the given glyph at each location, but erase the
898 * previous location's glyph.
899 * DISP_ALWAYS- Like DISP_FLASH, but vision is not taken into account.
902 #define TMP_AT_MAX_GLYPHS (COLNO * 2)
904 static struct tmp_glyph
{
905 coord saved
[TMP_AT_MAX_GLYPHS
]; /* previously updated positions */
906 int sidx
; /* index of next unused slot in saved[] */
907 int style
; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
908 int glyph
; /* glyph to use when printing */
909 struct tmp_glyph
*prev
;
916 static struct tmp_glyph
*tglyph
= (struct tmp_glyph
*) 0;
917 struct tmp_glyph
*tmp
;
926 else /* nested effect; we need dynamic memory */
927 tmp
= (struct tmp_glyph
*) alloc(sizeof(struct tmp_glyph
));
933 flush_screen(0); /* flush buffered glyphs */
936 case DISP_FREEMEM
: /* in case game ends with tmp_at() in progress */
939 if (tglyph
!= &tgfirst
)
940 free((genericptr_t
) tglyph
);
950 panic("tmp_at: tglyph not initialized");
958 if (tglyph
->style
== DISP_BEAM
|| tglyph
->style
== DISP_ALL
) {
961 /* Erase (reset) from source to end */
962 for (i
= 0; i
< tglyph
->sidx
; i
++)
963 newsym(tglyph
->saved
[i
].x
, tglyph
->saved
[i
].y
);
964 } else { /* DISP_FLASH or DISP_ALWAYS */
965 if (tglyph
->sidx
) /* been called at least once */
966 newsym(tglyph
->saved
[0].x
, tglyph
->saved
[0].y
);
968 /* tglyph->sidx = 0; -- about to be freed, so not necessary */
970 if (tglyph
!= &tgfirst
)
971 free((genericptr_t
) tglyph
);
978 if (tglyph
->style
== DISP_BEAM
|| tglyph
->style
== DISP_ALL
) {
979 if (tglyph
->style
!= DISP_ALL
&& !cansee(x
, y
))
981 if (tglyph
->sidx
>= TMP_AT_MAX_GLYPHS
)
982 break; /* too many locations */
983 /* save pos for later erasing */
984 tglyph
->saved
[tglyph
->sidx
].x
= x
;
985 tglyph
->saved
[tglyph
->sidx
].y
= y
;
987 } else { /* DISP_FLASH/ALWAYS */
988 if (tglyph
->sidx
) { /* not first call, so reset previous pos */
989 newsym(tglyph
->saved
[0].x
, tglyph
->saved
[0].y
);
990 tglyph
->sidx
= 0; /* display is presently up to date */
992 if (!cansee(x
, y
) && tglyph
->style
!= DISP_ALWAYS
)
994 tglyph
->saved
[0].x
= x
;
995 tglyph
->saved
[0].y
= y
;
999 show_glyph(x
, y
, tglyph
->glyph
); /* show it */
1000 flush_screen(0); /* make sure it shows up */
1008 * The hero is swallowed. Show a special graphics sequence for this. This
1009 * bypasses all of the display routines and messes with buffered screen
1010 * directly. This method works because both vision and display check for
1017 static xchar lastx
, lasty
; /* last swallowed position */
1018 int swallower
, left_ok
, rght_ok
;
1025 /* Clear old location */
1026 for (y
= lasty
- 1; y
<= lasty
+ 1; y
++)
1027 for (x
= lastx
- 1; x
<= lastx
+ 1; x
++)
1029 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1032 swallower
= monsndx(u
.ustuck
->data
);
1033 /* assume isok(u.ux,u.uy) */
1034 left_ok
= isok(u
.ux
- 1, u
.uy
);
1035 rght_ok
= isok(u
.ux
+ 1, u
.uy
);
1037 * Display the hero surrounded by the monster's stomach.
1039 if (isok(u
.ux
, u
.uy
- 1)) {
1041 show_glyph(u
.ux
- 1, u
.uy
- 1,
1042 swallow_to_glyph(swallower
, S_sw_tl
));
1043 show_glyph(u
.ux
, u
.uy
- 1, swallow_to_glyph(swallower
, S_sw_tc
));
1045 show_glyph(u
.ux
+ 1, u
.uy
- 1,
1046 swallow_to_glyph(swallower
, S_sw_tr
));
1050 show_glyph(u
.ux
- 1, u
.uy
, swallow_to_glyph(swallower
, S_sw_ml
));
1053 show_glyph(u
.ux
+ 1, u
.uy
, swallow_to_glyph(swallower
, S_sw_mr
));
1055 if (isok(u
.ux
, u
.uy
+ 1)) {
1057 show_glyph(u
.ux
- 1, u
.uy
+ 1,
1058 swallow_to_glyph(swallower
, S_sw_bl
));
1059 show_glyph(u
.ux
, u
.uy
+ 1, swallow_to_glyph(swallower
, S_sw_bc
));
1061 show_glyph(u
.ux
+ 1, u
.uy
+ 1,
1062 swallow_to_glyph(swallower
, S_sw_br
));
1065 /* Update the swallowed position. */
1073 * Similar to swallowed() in operation. Shows hero when underwater
1074 * except when in water level. Special routines exist for that.
1080 static xchar lastx
, lasty
;
1081 static boolean dela
;
1084 /* swallowing has a higher precedence than under water */
1085 if (Is_waterlevel(&u
.uz
) || u
.uswallow
)
1089 if (mode
== 1 || dela
) {
1093 /* delayed full update */
1094 } else if (mode
== 2) {
1098 /* limited update */
1100 for (y
= lasty
- 1; y
<= lasty
+ 1; y
++)
1101 for (x
= lastx
- 1; x
<= lastx
+ 1; x
++)
1103 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1106 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
1107 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++)
1108 if (isok(x
, y
) && is_pool(x
, y
)) {
1109 if (Blind
&& !(x
== u
.ux
&& y
== u
.uy
))
1110 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1121 * Very restricted display. You can only see yourself.
1127 static boolean dela
;
1129 /* swallowing has a higher precedence than under ground */
1134 if (mode
== 1 || dela
) {
1138 /* delayed full update */
1139 } else if (mode
== 2) {
1143 /* limited update */
1149 /* =========================================================================
1153 * Loop through all of the monsters and update them. Called when:
1154 * + going blind & telepathic
1155 * + regaining sight & telepathic
1156 * + getting and losing infravision
1158 * + doing a full screen redraw
1159 * + see invisible times out or a ring of see invisible is taken off
1160 * + when a potion of see invisible is quaffed or a ring of see
1161 * invisible is put on
1162 * + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1163 * + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1169 register struct monst
*mon
;
1170 int new_warn_obj_cnt
= 0;
1172 if (defer_see_monsters
)
1175 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
1176 if (DEADMONSTER(mon
))
1178 newsym(mon
->mx
, mon
->my
);
1181 if (Warn_of_mon
&& (context
.warntype
.obj
& mon
->data
->mflags2
) != 0L)
1185 * Make Sting glow blue or stop glowing if required.
1187 if (new_warn_obj_cnt
!= warn_obj_cnt
) {
1188 Sting_effects(new_warn_obj_cnt
);
1189 warn_obj_cnt
= new_warn_obj_cnt
;
1192 /* when mounted, hero's location gets caught by monster loop */
1198 * Block/unblock light depending on what a mimic is mimicing and if it's
1199 * invisible or not. Should be called only when the state of See_invisible
1203 set_mimic_blocking()
1205 register struct monst
*mon
;
1207 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
1208 if (DEADMONSTER(mon
))
1210 if (mon
->minvis
&& is_lightblocker_mappear(mon
)) {
1212 block_point(mon
->mx
, mon
->my
);
1214 unblock_point(mon
->mx
, mon
->my
);
1220 * Loop through all of the object *locations* and update them. Called when
1226 register struct obj
*obj
;
1227 for (obj
= fobj
; obj
; obj
= obj
->nobj
)
1228 if (vobj_at(obj
->ox
, obj
->oy
) == obj
)
1229 newsym(obj
->ox
, obj
->oy
);
1233 * Update hallucinated traps.
1241 for (trap
= ftrap
; trap
; trap
= trap
->ntrap
) {
1242 glyph
= glyph_at(trap
->tx
, trap
->ty
);
1243 if (glyph_is_trap(glyph
))
1244 newsym(trap
->tx
, trap
->ty
);
1249 * Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1254 flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1268 register struct rm
*lev
;
1271 return; /* display isn't ready yet */
1277 if (Underwater
&& !Is_waterlevel(&u
.uz
)) {
1286 /* shut down vision */
1290 * This routine assumes that cls() does the following:
1291 * + fills the physical screen with the symbol for rock
1292 * + clears the glyph buffer
1296 /* display memory */
1297 for (x
= 1; x
< COLNO
; x
++) {
1299 for (y
= 0; y
< ROWNO
; y
++, lev
++)
1300 if (lev
->glyph
!= cmap_to_glyph(S_stone
))
1301 show_glyph(x
, y
, lev
->glyph
);
1304 /* see what is to be seen */
1307 /* overlay with monsters */
1310 context
.botlx
= 1; /* force a redraw of the bottom line */
1313 /* =========================================================================
1315 /* Glyph Buffering (3rd screen) ============================================
1319 xchar
new; /* perhaps move this bit into the rm structure. */
1323 static gbuf_entry gbuf
[ROWNO
][COLNO
];
1324 static char gbuf_start
[ROWNO
];
1325 static char gbuf_stop
[ROWNO
];
1327 /* FIXME: This is a dirty hack, because newsym() doesn't distinguish
1328 * between object piles and single objects, it doesn't mark the location
1336 if (gbuf_start
[y
] > x
)
1338 if (gbuf_stop
[y
] < x
)
1343 * Store the glyph in the 3rd screen for later flushing.
1346 show_glyph(x
, y
, glyph
)
1350 * Check for bad positions and glyphs.
1356 /* column 0 is invalid, but it's often used as a flag, so ignore it */
1361 * This assumes an ordering of the offsets. See display.h for
1365 if (glyph
>= GLYPH_WARNING_OFF
1366 && glyph
< GLYPH_STATUE_OFF
) { /* a warning */
1368 offset
= glyph
- GLYPH_WARNING_OFF
;
1369 } else if (glyph
>= GLYPH_SWALLOW_OFF
) { /* swallow border */
1370 text
= "swallow border";
1371 offset
= glyph
- GLYPH_SWALLOW_OFF
;
1372 } else if (glyph
>= GLYPH_ZAP_OFF
) { /* zap beam */
1374 offset
= glyph
- GLYPH_ZAP_OFF
;
1375 } else if (glyph
>= GLYPH_EXPLODE_OFF
) { /* explosion */
1377 offset
= glyph
- GLYPH_EXPLODE_OFF
;
1378 } else if (glyph
>= GLYPH_CMAP_OFF
) { /* cmap */
1379 text
= "cmap_index";
1380 offset
= glyph
- GLYPH_CMAP_OFF
;
1381 } else if (glyph
>= GLYPH_OBJ_OFF
) { /* object */
1383 offset
= glyph
- GLYPH_OBJ_OFF
;
1384 } else if (glyph
>= GLYPH_RIDDEN_OFF
) { /* ridden mon */
1385 text
= "ridden mon";
1386 offset
= glyph
- GLYPH_RIDDEN_OFF
;
1387 } else if (glyph
>= GLYPH_BODY_OFF
) { /* a corpse */
1389 offset
= glyph
- GLYPH_BODY_OFF
;
1390 } else if (glyph
>= GLYPH_DETECT_OFF
) { /* detected mon */
1391 text
= "detected mon";
1392 offset
= glyph
- GLYPH_DETECT_OFF
;
1393 } else if (glyph
>= GLYPH_INVIS_OFF
) { /* invisible mon */
1394 text
= "invisible mon";
1395 offset
= glyph
- GLYPH_INVIS_OFF
;
1396 } else if (glyph
>= GLYPH_PET_OFF
) { /* a pet */
1398 offset
= glyph
- GLYPH_PET_OFF
;
1399 } else { /* a monster */
1404 impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].", x
, y
,
1405 glyph
, text
, offset
);
1409 if (glyph
>= MAX_GLYPH
) {
1410 impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).", glyph
,
1415 if (gbuf
[y
][x
].glyph
!= glyph
|| iflags
.use_background_glyph
) {
1416 gbuf
[y
][x
].glyph
= glyph
;
1418 if (gbuf_start
[y
] > x
)
1420 if (gbuf_stop
[y
] < x
)
1426 * Reset the changed glyph borders so that none of the 3rd screen has
1429 #define reset_glyph_bbox() \
1433 for (i = 0; i < ROWNO; i++) { \
1434 gbuf_start[i] = COLNO - 1; \
1439 static gbuf_entry nul_gbuf
= { 0, cmap_to_glyph(S_stone
) };
1441 * Turn the 3rd screen into stone.
1444 clear_glyph_buffer()
1447 register gbuf_entry
*gptr
;
1449 for (y
= 0; y
< ROWNO
; y
++) {
1451 for (x
= COLNO
; x
; x
--) {
1459 * Assumes that the indicated positions are filled with S_stone glyphs.
1462 row_refresh(start
, stop
, y
)
1467 for (x
= start
; x
<= stop
; x
++)
1468 if (gbuf
[y
][x
].glyph
!= cmap_to_glyph(S_stone
))
1469 print_glyph(WIN_MAP
, x
, y
, gbuf
[y
][x
].glyph
, get_bk_glyph(x
,y
));
1475 static boolean in_cls
= 0;
1480 display_nhwindow(WIN_MESSAGE
, FALSE
); /* flush messages */
1481 context
.botlx
= 1; /* force update of botl window */
1482 clear_nhwindow(WIN_MAP
); /* clear physical screen */
1484 clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1489 * Synch the third screen with the display.
1492 flush_screen(cursor_on_u
)
1495 /* Prevent infinite loops on errors:
1496 * flush_screen->print_glyph->impossible->pline->flush_screen
1498 static boolean flushing
= 0;
1499 static boolean delay_flushing
= 0;
1502 if (cursor_on_u
== -1)
1503 delay_flushing
= !delay_flushing
;
1507 return; /* if already flushing then return */
1509 #ifdef HANGUPHANDLING
1510 if (program_state
.done_hup
)
1514 for (y
= 0; y
< ROWNO
; y
++) {
1515 register gbuf_entry
*gptr
= &gbuf
[y
][x
= gbuf_start
[y
]];
1516 for (; x
<= gbuf_stop
[y
]; gptr
++, x
++)
1518 print_glyph(WIN_MAP
, x
, y
, gptr
->glyph
, get_bk_glyph(x
, y
));
1524 curs(WIN_MAP
, u
.ux
, u
.uy
); /* move cursor to the hero */
1525 display_nhwindow(WIN_MAP
, FALSE
);
1528 if (context
.botl
|| context
.botlx
)
1532 /* =========================================================================
1538 * Use the information in the rm structure at the given position to create
1539 * a glyph of a background.
1541 * I had to add a field in the rm structure (horizontal) so that we knew
1542 * if open doors and secret doors were horizontal or vertical. Previously,
1543 * the screen symbol had the horizontal/vertical information set at
1544 * level generation time.
1546 * I used the 'ladder' field (really doormask) for deciding if stairwells
1547 * were up or down. I didn't want to check the upstairs and dnstairs
1555 struct rm
*ptr
= &(levl
[x
][y
]);
1560 idx
= level
.flags
.arboreal
? S_tree
: S_stone
;
1566 idx
= (ptr
->waslit
|| flags
.lit_corridor
) ? S_litcorr
: S_corr
;
1580 idx
= ptr
->seenv
? wall_angle(ptr
) : S_stone
;
1583 if (ptr
->doormask
) {
1584 if (ptr
->doormask
& D_BROKEN
)
1586 else if (ptr
->doormask
& D_ISOPEN
)
1587 idx
= (ptr
->horizontal
) ? S_hodoor
: S_vodoor
;
1588 else /* else is closed */
1589 idx
= (ptr
->horizontal
) ? S_hcdoor
: S_vcdoor
;
1604 idx
= (ptr
->ladder
& LA_DOWN
) ? S_dnstair
: S_upstair
;
1607 idx
= (ptr
->ladder
& LA_DOWN
) ? S_dnladder
: S_upladder
;
1640 idx
= (ptr
->horizontal
) ? S_hcdbridge
: S_vcdbridge
;
1643 switch (ptr
->drawbridgemask
& DB_UNDER
) {
1657 impossible("Strange db-under: %d",
1658 ptr
->drawbridgemask
& DB_UNDER
);
1659 idx
= S_room
; /* something is better than nothing */
1663 case DRAWBRIDGE_DOWN
:
1664 idx
= (ptr
->horizontal
) ? S_hodbridge
: S_vodbridge
;
1667 impossible("back_to_glyph: unknown level type [ = %d ]", ptr
->typ
);
1672 return cmap_to_glyph(idx
);
1676 * swallow_to_glyph()
1678 * Convert a monster number and a swallow location into the correct glyph.
1679 * If you don't want a patchwork monster while hallucinating, decide on
1680 * a random monster in swallowed() and don't use what_mon() here.
1683 swallow_to_glyph(mnum
, loc
)
1687 if (loc
< S_sw_tl
|| S_sw_br
< loc
) {
1688 impossible("swallow_to_glyph: bad swallow location");
1691 return ((int) (what_mon(mnum
) << 3) | (loc
- S_sw_tl
)) + GLYPH_SWALLOW_OFF
;
1697 * Change the given zap direction and beam type into a glyph. Each beam
1698 * type has four glyphs, one for each of the symbols below. The order of
1699 * the zap symbols [0-3] as defined in rm.h are:
1701 * | S_vbeam ( 0, 1) or ( 0,-1)
1702 * - S_hbeam ( 1, 0) or (-1, 0)
1703 * \ S_lslant ( 1, 1) or (-1,-1)
1704 * / S_rslant (-1, 1) or ( 1,-1)
1707 zapdir_to_glyph(dx
, dy
, beam_type
)
1708 register int dx
, dy
;
1711 if (beam_type
>= NUM_ZAP
) {
1712 impossible("zapdir_to_glyph: illegal beam type");
1715 dx
= (dx
== dy
) ? 2 : (dx
&& dy
) ? 3 : dx
? 1 : 0;
1717 return ((int) ((beam_type
<< 2) | dx
)) + GLYPH_ZAP_OFF
;
1721 * Utility routine for dowhatis() used to find out the glyph displayed at
1722 * the location. This isn't necessarily the same as the glyph in the levl
1723 * structure, so we must check the "third screen".
1729 if (x
< 0 || y
< 0 || x
>= COLNO
|| y
>= ROWNO
)
1730 return cmap_to_glyph(S_room
); /* XXX */
1731 return gbuf
[y
][x
].glyph
;
1735 * This will be used to get the glyph for the background so that
1736 * it can potentially be merged into graphical window ports
1737 * to improve the appearance of stuff on dark room
1738 * squares and the plane of air etc.
1740 * Until that is working correctly in the branch, however, for now
1741 * we just return NO_GLYPH as an indicator to ignore it.
1748 int idx
, bkglyph
= NO_GLYPH
;
1749 struct rm
*lev
= &levl
[x
][y
];
1751 if (iflags
.use_background_glyph
&& lev
->seenv
!= 0
1752 && gbuf
[y
][x
].glyph
!= cmap_to_glyph(S_stone
)) {
1756 idx
= level
.flags
.arboreal
? S_tree
: S_stone
;
1762 idx
= (lev
->waslit
|| flags
.lit_corridor
) ? S_litcorr
: S_corr
;
1788 if (!cansee(x
, y
) && (!lev
->waslit
|| flags
.dark_room
)) {
1789 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
1790 if (lev
->typ
== CORR
&& idx
== S_litcorr
)
1792 else if (idx
== S_room
)
1793 idx
= (flags
.dark_room
&& iflags
.use_color
)
1794 ? DARKROOMSYM
: S_stone
;
1798 bkglyph
= cmap_to_glyph(idx
);
1803 /* -------------------------------------------------------------------------
1805 /* Wall Angle --------------------------------------------------------------
1810 static const char *FDECL(type_to_name
, (int));
1811 static void FDECL(error4
, (int, int, int, int, int, int));
1813 static int bad_count
[MAX_TYPE
]; /* count of positions flagged as bad */
1814 static const char *type_names
[MAX_TYPE
] = {
1815 "STONE", "VWALL", "HWALL", "TLCORNER", "TRCORNER", "BLCORNER", "BRCORNER",
1816 "CROSSWALL", "TUWALL", "TDWALL", "TLWALL", "TRWALL", "DBWALL", "TREE",
1817 "SDOOR", "SCORR", "POOL", "MOAT", "WATER", "DRAWBRIDGE_UP", "LAVAPOOL",
1818 "IRON_BARS", "DOOR", "CORR", "ROOM", "STAIRS", "LADDER", "FOUNTAIN",
1819 "THRONE", "SINK", "GRAVE", "ALTAR", "ICE", "DRAWBRIDGE_DOWN", "AIR",
1827 return (type
< 0 || type
>= MAX_TYPE
) ? "unknown" : type_names
[type
];
1831 error4(x
, y
, a
, b
, c
, dd
)
1832 int x
, y
, a
, b
, c
, dd
;
1834 pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1835 type_to_name(levl
[x
][y
].typ
), x
, y
,
1836 a
? "1" : "", b
? "2" : "", c
? "3" : "", dd
? "4" : "");
1837 bad_count
[levl
[x
][y
].typ
]++;
1839 #endif /* WA_VERBOSE */
1842 * Return 'which' if position is implies an unfinished exterior. Return
1843 * zero otherwise. Unfinished implies outer area is rock or a corridor.
1845 * Things that are ambiguous: lava
1848 check_pos(x
, y
, which
)
1854 type
= levl
[x
][y
].typ
;
1855 if (IS_ROCK(type
) || type
== CORR
|| type
== SCORR
)
1860 /* Return TRUE if more than one is non-zero. */
1864 more_than_one(x
, y
, a
, b
, c
)
1867 if ((a
&& (b
| c
)) || (b
&& (a
| c
)) || (c
&& (a
| b
))) {
1868 error4(x
, y
, a
, b
, c
, 0);
1874 #define more_than_one(x, y, a, b, c) \
1875 (((a) && ((b) | (c))) || ((b) && ((a) | (c))) || ((c) && ((a) | (b))))
1878 /* Return the wall mode for a T wall. */
1880 set_twall(x0
, y0
, x1
, y1
, x2
, y2
, x3
, y3
)
1881 int x0
, y0
; /* used #if WA_VERBOSE */
1882 int x1
, y1
, x2
, y2
, x3
, y3
;
1884 int wmode
, is_1
, is_2
, is_3
;
1889 is_1
= check_pos(x1
, y1
, WM_T_LONG
);
1890 is_2
= check_pos(x2
, y2
, WM_T_BL
);
1891 is_3
= check_pos(x3
, y3
, WM_T_BR
);
1892 if (more_than_one(x0
, y0
, is_1
, is_2
, is_3
)) {
1895 wmode
= is_1
+ is_2
+ is_3
;
1900 /* Return wall mode for a horizontal or vertical wall. */
1902 set_wall(x
, y
, horiz
)
1905 int wmode
, is_1
, is_2
;
1908 is_1
= check_pos(x
, y
- 1, WM_W_TOP
);
1909 is_2
= check_pos(x
, y
+ 1, WM_W_BOTTOM
);
1911 is_1
= check_pos(x
- 1, y
, WM_W_LEFT
);
1912 is_2
= check_pos(x
+ 1, y
, WM_W_RIGHT
);
1914 if (more_than_one(x
, y
, is_1
, is_2
, 0)) {
1917 wmode
= is_1
+ is_2
;
1922 /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
1924 set_corn(x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
)
1925 int x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1927 int wmode
, is_1
, is_2
, is_3
, is_4
;
1929 is_1
= check_pos(x1
, y1
, 1);
1930 is_2
= check_pos(x2
, y2
, 1);
1931 is_3
= check_pos(x3
, y3
, 1);
1932 is_4
= check_pos(x4
, y4
, 1); /* inner location */
1935 * All 4 should not be true. So if the inner location is rock,
1936 * use it. If all of the outer 3 are true, use outer. We currently
1937 * can't cover the case where only part of the outer is rock, so
1938 * we just say that all the walls are finished (if not overridden
1939 * by the inner section).
1943 } else if (is_1
&& is_2
&& is_3
)
1946 wmode
= 0; /* finished walls on all sides */
1951 /* Return mode for a crosswall. */
1956 int wmode
, is_1
, is_2
, is_3
, is_4
;
1958 is_1
= check_pos(x
- 1, y
- 1, 1);
1959 is_2
= check_pos(x
+ 1, y
- 1, 1);
1960 is_3
= check_pos(x
+ 1, y
+ 1, 1);
1961 is_4
= check_pos(x
- 1, y
+ 1, 1);
1963 wmode
= is_1
+ is_2
+ is_3
+ is_4
;
1965 if (is_1
&& is_3
&& (is_2
+ is_4
== 0)) {
1967 } else if (is_2
&& is_4
&& (is_1
+ is_3
== 0)) {
1971 error4(x
, y
, is_1
, is_2
, is_3
, is_4
);
1987 /* Called from mklev. Scan the level and set the wall modes. */
1996 for (x
= 0; x
< MAX_TYPE
; x
++)
2000 for (x
= 0; x
< COLNO
; x
++)
2001 for (lev
= &levl
[x
][0], y
= 0; y
< ROWNO
; y
++, lev
++) {
2004 wmode
= set_wall(x
, y
, (int) lev
->horizontal
);
2007 wmode
= set_wall(x
, y
, 0);
2010 wmode
= set_wall(x
, y
, 1);
2013 wmode
= set_twall(x
, y
, x
, y
- 1, x
- 1, y
+ 1, x
+ 1, y
+ 1);
2016 wmode
= set_twall(x
, y
, x
, y
+ 1, x
+ 1, y
- 1, x
- 1, y
- 1);
2019 wmode
= set_twall(x
, y
, x
+ 1, y
, x
- 1, y
- 1, x
- 1, y
+ 1);
2022 wmode
= set_twall(x
, y
, x
- 1, y
, x
+ 1, y
+ 1, x
+ 1, y
- 1);
2026 set_corn(x
- 1, y
- 1, x
, y
- 1, x
- 1, y
, x
+ 1, y
+ 1);
2030 set_corn(x
, y
- 1, x
+ 1, y
- 1, x
+ 1, y
, x
- 1, y
+ 1);
2034 set_corn(x
, y
+ 1, x
- 1, y
+ 1, x
- 1, y
, x
+ 1, y
- 1);
2038 set_corn(x
+ 1, y
, x
+ 1, y
+ 1, x
, y
+ 1, x
- 1, y
- 1);
2041 wmode
= set_crosswall(x
, y
);
2045 wmode
= -1; /* don't set wall info */
2050 lev
->wall_info
= (lev
->wall_info
& ~WM_MASK
) | wmode
;
2054 /* check if any bad positions found */
2055 for (x
= y
= 0; x
< MAX_TYPE
; x
++)
2058 y
= 1; /* only print once */
2059 pline("set_wall_type: wall mode problems with: ");
2061 pline("%s %d;", type_names
[x
], bad_count
[x
]);
2063 #endif /* WA_VERBOSE */
2066 /* -------------------------------------------------------------------------
2068 /* This matrix is used here and in vision.c. */
2069 unsigned char seenv_matrix
[3][3] = { { SV2
, SV1
, SV0
},
2070 { SV3
, SVALL
, SV7
},
2071 { SV4
, SV5
, SV6
} };
2073 #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
2075 /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
2077 set_seenv(lev
, x0
, y0
, x
, y
)
2079 int x0
, y0
, x
, y
; /* from, to */
2081 int dx
= x
- x0
, dy
= y0
- y
;
2083 lev
->seenv
|= seenv_matrix
[sign(dy
) + 1][sign(dx
) + 1];
2086 /* Called by blackout(vault.c) when vault guard removes temporary corridor,
2087 turning spot <x0,y0> back into stone; <x1,y1> is an adjacent spot. */
2089 unset_seenv(lev
, x0
, y0
, x1
, y1
)
2090 struct rm
*lev
; /* &levl[x1][y1] */
2091 int x0
, y0
, x1
, y1
; /* from, to; abs(x1-x0)==1 && abs(y0-y1)==1 */
2093 int dx
= x1
- x0
, dy
= y0
- y1
;
2095 lev
->seenv
&= ~seenv_matrix
[dy
+ 1][dx
+ 1];
2098 /* -------------------------------------------------------------------------
2101 /* T wall types, one for each row in wall_matrix[][]. */
2108 * These are the column names of wall_matrix[][]. They are the "results"
2109 * of a tdwall pattern match. All T walls are rotated so they become
2110 * a tdwall. Then we do a single pattern match, but return the
2111 * correct result for the original wall by using different rows for
2112 * each of the wall types.
2120 static const int wall_matrix
[4][5] = {
2121 { S_stone
, S_tlcorn
, S_trcorn
, S_hwall
, S_tdwall
}, /* tdwall */
2122 { S_stone
, S_trcorn
, S_brcorn
, S_vwall
, S_tlwall
}, /* tlwall */
2123 { S_stone
, S_brcorn
, S_blcorn
, S_hwall
, S_tuwall
}, /* tuwall */
2124 { S_stone
, S_blcorn
, S_tlcorn
, S_vwall
, S_trwall
}, /* trwall */
2127 /* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][].
2135 * These are the column names for cross_matrix[][]. They express results
2136 * in C_br (bottom right) terms. All crosswalls with a single solid
2137 * quarter are rotated so the solid section is at the bottom right.
2138 * We pattern match on that, but return the correct result depending
2139 * on which row we'ere looking at.
2148 static const int cross_matrix
[4][6] = {
2149 { S_brcorn
, S_blcorn
, S_tlcorn
, S_tuwall
, S_trwall
, S_crwall
},
2150 { S_blcorn
, S_tlcorn
, S_trcorn
, S_trwall
, S_tdwall
, S_crwall
},
2151 { S_tlcorn
, S_trcorn
, S_brcorn
, S_tdwall
, S_tlwall
, S_crwall
},
2152 { S_trcorn
, S_brcorn
, S_blcorn
, S_tlwall
, S_tuwall
, S_crwall
},
2155 /* Print out a T wall warning and all interesting info. */
2160 static const char warn_str
[] = "wall_angle: %s: case %d: seenv = 0x%x";
2163 if (lev
->typ
== TUWALL
)
2165 else if (lev
->typ
== TLWALL
)
2167 else if (lev
->typ
== TRWALL
)
2169 else if (lev
->typ
== TDWALL
)
2173 impossible(warn_str
, wname
, lev
->wall_info
& WM_MASK
,
2174 (unsigned int) lev
->seenv
);
2178 * Return the correct graphics character index using wall type, wall mode,
2179 * and the seen vector. It is expected that seenv is non zero.
2181 * All T-wall vectors are rotated to be TDWALL. All single crosswall
2182 * blocks are rotated to bottom right. All double crosswall are rotated
2183 * to W_X_BLTR. All results are converted back.
2185 * The only way to understand this is to take out pen and paper and
2186 * draw diagrams. See rm.h for more details on the wall modes and
2193 register unsigned int seenv
= lev
->seenv
& 0xff;
2197 #define only(sv, bits) (((sv) & (bits)) && !((sv) & ~(bits)))
2200 row
= wall_matrix
[T_u
];
2201 seenv
= (seenv
>> 4 | seenv
<< 4) & 0xff; /* rotate to tdwall */
2204 row
= wall_matrix
[T_l
];
2205 seenv
= (seenv
>> 2 | seenv
<< 6) & 0xff; /* rotate to tdwall */
2208 row
= wall_matrix
[T_r
];
2209 seenv
= (seenv
>> 6 | seenv
<< 2) & 0xff; /* rotate to tdwall */
2212 row
= wall_matrix
[T_d
];
2214 switch (lev
->wall_info
& WM_MASK
) {
2218 } else if (seenv
== SV6
) {
2220 } else if (seenv
& (SV3
| SV5
| SV7
)
2221 || ((seenv
& SV4
) && (seenv
& SV6
))) {
2223 } else if (seenv
& (SV0
| SV1
| SV2
)) {
2224 col
= (seenv
& (SV4
| SV6
) ? T_tdwall
: T_hwall
);
2231 if (seenv
& (SV3
| SV4
) && !(seenv
& (SV5
| SV6
| SV7
))) {
2233 } else if (seenv
& (SV6
| SV7
) && !(seenv
& (SV3
| SV4
| SV5
))) {
2235 } else if ((seenv
& SV5
)
2236 || ((seenv
& (SV3
| SV4
)) && (seenv
& (SV6
| SV7
)))) {
2239 /* only SV0|SV1|SV2 */
2240 if (!only(seenv
, SV0
| SV1
| SV2
))
2246 #if 0 /* older method, fixed */
2247 if (only(seenv
, SV4
|SV5
)) {
2249 } else if ((seenv
& (SV0
|SV1
|SV2
))
2250 && only(seenv
, SV0
|SV1
|SV2
|SV6
|SV7
)) {
2252 } else if ((seenv
& SV3
)
2253 || ((seenv
& (SV0
|SV1
|SV2
)) && (seenv
& (SV4
|SV5
)))) {
2261 if (only(seenv
, SV4
| SV5
))
2263 else if ((seenv
& (SV0
| SV1
| SV2
| SV7
))
2264 && !(seenv
& (SV3
| SV4
| SV5
)))
2266 else if (only(seenv
, SV6
))
2272 #if 0 /* older method, fixed */
2273 if (only(seenv
, SV5
|SV6
)) {
2275 } else if ((seenv
& (SV0
|SV1
|SV2
))
2276 && only(seenv
, SV0
|SV1
|SV2
|SV3
|SV4
)) {
2278 } else if ((seenv
& SV7
)
2279 || ((seenv
& (SV0
|SV1
|SV2
)) && (seenv
& (SV5
|SV6
)))) {
2287 if (only(seenv
, SV5
| SV6
))
2289 else if ((seenv
& (SV0
| SV1
| SV2
| SV3
))
2290 && !(seenv
& (SV5
| SV6
| SV7
)))
2292 else if (only(seenv
, SV4
))
2299 impossible("wall_angle: unknown T wall mode %d",
2300 lev
->wall_info
& WM_MASK
);
2308 if (lev
->horizontal
)
2312 switch (lev
->wall_info
& WM_MASK
) {
2314 idx
= seenv
? S_vwall
: S_stone
;
2317 idx
= seenv
& (SV1
| SV2
| SV3
| SV4
| SV5
) ? S_vwall
: S_stone
;
2320 idx
= seenv
& (SV0
| SV1
| SV5
| SV6
| SV7
) ? S_vwall
: S_stone
;
2323 impossible("wall_angle: unknown vwall mode %d",
2324 lev
->wall_info
& WM_MASK
);
2332 switch (lev
->wall_info
& WM_MASK
) {
2334 idx
= seenv
? S_hwall
: S_stone
;
2337 idx
= seenv
& (SV3
| SV4
| SV5
| SV6
| SV7
) ? S_hwall
: S_stone
;
2340 idx
= seenv
& (SV0
| SV1
| SV2
| SV3
| SV7
) ? S_hwall
: S_stone
;
2343 impossible("wall_angle: unknown hwall mode %d",
2344 lev
->wall_info
& WM_MASK
);
2350 #define set_corner(idx, lev, which, outer, inner, name) \
2351 switch ((lev)->wall_info & WM_MASK) { \
2356 idx = seenv & (outer) ? which : S_stone; \
2359 idx = seenv & ~(inner) ? which : S_stone; \
2362 impossible("wall_angle: unknown %s mode %d", name, \
2363 (lev)->wall_info &WM_MASK); \
2369 set_corner(idx
, lev
, S_tlcorn
, (SV3
| SV4
| SV5
), SV4
, "tlcorn");
2372 set_corner(idx
, lev
, S_trcorn
, (SV5
| SV6
| SV7
), SV6
, "trcorn");
2375 set_corner(idx
, lev
, S_blcorn
, (SV1
| SV2
| SV3
), SV2
, "blcorn");
2378 set_corner(idx
, lev
, S_brcorn
, (SV7
| SV0
| SV1
), SV0
, "brcorn");
2382 switch (lev
->wall_info
& WM_MASK
) {
2386 else if (seenv
== SV2
)
2388 else if (seenv
== SV4
)
2390 else if (seenv
== SV6
)
2392 else if (!(seenv
& ~(SV0
| SV1
| SV2
))
2393 && (seenv
& SV1
|| seenv
== (SV0
| SV2
)))
2395 else if (!(seenv
& ~(SV2
| SV3
| SV4
))
2396 && (seenv
& SV3
|| seenv
== (SV2
| SV4
)))
2398 else if (!(seenv
& ~(SV4
| SV5
| SV6
))
2399 && (seenv
& SV5
|| seenv
== (SV4
| SV6
)))
2401 else if (!(seenv
& ~(SV0
| SV6
| SV7
))
2402 && (seenv
& SV7
|| seenv
== (SV0
| SV6
)))
2409 row
= cross_matrix
[C_tl
];
2410 seenv
= (seenv
>> 4 | seenv
<< 4) & 0xff;
2413 row
= cross_matrix
[C_tr
];
2414 seenv
= (seenv
>> 6 | seenv
<< 2) & 0xff;
2417 row
= cross_matrix
[C_bl
];
2418 seenv
= (seenv
>> 2 | seenv
<< 6) & 0xff;
2421 row
= cross_matrix
[C_br
];
2426 seenv
= seenv
& ~SV4
; /* strip SV4 */
2429 } else if (seenv
& (SV2
| SV3
)) {
2430 if (seenv
& (SV5
| SV6
| SV7
))
2432 else if (seenv
& (SV0
| SV1
))
2436 } else if (seenv
& (SV5
| SV6
)) {
2437 if (seenv
& (SV1
| SV2
| SV3
))
2439 else if (seenv
& (SV0
| SV7
))
2443 } else if (seenv
& SV1
) {
2444 col
= seenv
& SV7
? C_crwall
: C_tuwall
;
2445 } else if (seenv
& SV7
) {
2446 col
= seenv
& SV1
? C_crwall
: C_tlwall
;
2448 impossible("wall_angle: bottom of crwall check");
2457 if (only(seenv
, SV1
| SV2
| SV3
))
2459 else if (only(seenv
, SV5
| SV6
| SV7
))
2461 else if (only(seenv
, SV0
| SV4
))
2468 if (only(seenv
, SV0
| SV1
| SV7
))
2470 else if (only(seenv
, SV3
| SV4
| SV5
))
2472 else if (only(seenv
, SV2
| SV6
))
2479 impossible("wall_angle: unknown crosswall mode");
2486 impossible("wall_angle: unexpected wall type %d", lev
->typ
);