1 /* NetHack 3.6 display.c $NHDT-Date: 1463614572 2016/05/18 23:36:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.86 $ */
2 /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */
3 /* and Dave Cohrs, 1990. */
4 /* NetHack may be freely redistributed. See license for details. */
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 hero's memory of an invisible monster is accurate, we want to keep
534 * him from detecting the same monster over and over again on each turn.
535 * We must return (so we don't erase the monster). (We must also, in the
536 * search function, be sure to skip over previously detected 'I's.)
538 if (glyph_is_invisible(lev
->glyph
) && m_at(x
, y
))
541 /* The hero can't feel non pool locations while under water
542 except for lava and ice. */
543 if (Underwater
&& !Is_waterlevel(&u
.uz
)
544 && !is_pool_or_lava(x
, y
) && !is_ice(x
, y
))
547 /* Set the seen vector as if the hero had seen it.
548 It doesn't matter if the hero is levitating or not. */
549 set_seenv(lev
, u
.ux
, u
.uy
, x
, y
);
551 if (!can_reach_floor(FALSE
)) {
553 * Levitation Rules. It is assumed that the hero can feel the state
554 * of the walls around herself and can tell if she is in a corridor,
555 * room, or doorway. Boulders are felt because they are large enough.
556 * Anything else is unknown because the hero can't reach the ground.
557 * This makes things difficult.
559 * Check (and display) in order:
561 * + Stone, walls, and closed doors.
562 * + Boulders. [see a boulder before a doorway]
564 * + Room/water positions
565 * + Everything else (hallways!)
567 if (IS_ROCK(lev
->typ
)
568 || (IS_DOOR(lev
->typ
)
569 && (lev
->doormask
& (D_LOCKED
| D_CLOSED
)))) {
570 map_background(x
, y
, 1);
571 } else if ((boulder
= sobj_at(BOULDER
, x
, y
)) != 0) {
572 map_object(boulder
, 1);
573 } else if (IS_DOOR(lev
->typ
)) {
574 map_background(x
, y
, 1);
575 } else if (IS_ROOM(lev
->typ
) || IS_POOL(lev
->typ
)) {
576 boolean do_room_glyph
;
579 * An open room or water location. Normally we wouldn't touch
580 * this, but we have to get rid of remembered boulder symbols.
581 * This will only occur in rare occasions when the hero goes
582 * blind and doesn't find a boulder where expected (something
583 * came along and picked it up). We know that there is not a
584 * boulder at this location. Show fountains, pools, etc.
585 * underneath if already seen. Otherwise, show the appropriate
588 * Similarly, if the hero digs a hole in a wall or feels a
590 * that used to contain an unseen monster. In these cases,
591 * there's no reason to assume anything was underneath, so
592 * just show the appropriate floor symbol. If something was
593 * embedded in the wall, the glyph will probably already
594 * reflect that. Don't change the symbol in this case.
596 * This isn't quite correct. If the boulder was on top of some
597 * other objects they should be seen once the boulder is removed.
598 * However, we have no way of knowing that what is there now
599 * was there then. So we let the hero have a lapse of memory.
600 * We could also just display what is currently on the top of the
601 * object stack (if anything).
603 do_room_glyph
= FALSE
;
604 if (lev
->glyph
== objnum_to_glyph(BOULDER
)
605 || glyph_is_invisible(lev
->glyph
)) {
606 if (lev
->typ
!= ROOM
&& lev
->seenv
)
607 map_background(x
, y
, 1);
609 do_room_glyph
= TRUE
;
610 } else if (lev
->glyph
>= cmap_to_glyph(S_stone
)
611 && lev
->glyph
< cmap_to_glyph(S_darkroom
)) {
612 do_room_glyph
= TRUE
;
615 lev
->glyph
= (flags
.dark_room
&& iflags
.use_color
616 && !Is_rogue_level(&u
.uz
))
617 ? cmap_to_glyph(S_darkroom
)
618 : (lev
->waslit
? cmap_to_glyph(S_room
)
619 : cmap_to_glyph(S_stone
));
620 show_glyph(x
, y
, lev
->glyph
);
623 /* We feel it (I think hallways are the only things left). */
624 map_background(x
, y
, 1);
625 /* Corridors are never felt as lit (unless remembered that way) */
626 /* (lit_corridor only). */
627 if (lev
->typ
== CORR
&& lev
->glyph
== cmap_to_glyph(S_litcorr
)
629 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
630 else if (lev
->typ
== ROOM
&& flags
.dark_room
&& iflags
.use_color
631 && lev
->glyph
== cmap_to_glyph(S_room
))
632 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_darkroom
));
635 _map_location(x
, y
, 1);
639 * A ball or chain is only felt if it is first on the object
640 * location list. Otherwise, we need to clear the felt bit ---
641 * something has been dropped on the ball/chain. If the bit is
642 * not cleared, then when the ball/chain is moved it will drop
645 if (uchain
->ox
== x
&& uchain
->oy
== y
) {
646 if (level
.objects
[x
][y
] == uchain
)
647 u
.bc_felt
|= BC_CHAIN
;
649 u
.bc_felt
&= ~BC_CHAIN
; /* do not feel the chain */
651 if (!carried(uball
) && uball
->ox
== x
&& uball
->oy
== y
) {
652 if (level
.objects
[x
][y
] == uball
)
653 u
.bc_felt
|= BC_BALL
;
655 u
.bc_felt
&= ~BC_BALL
; /* do not feel the ball */
659 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
660 if (lev
->typ
== ROOM
&& lev
->glyph
== cmap_to_glyph(S_room
)
661 && (!lev
->waslit
|| (flags
.dark_room
&& iflags
.use_color
)))
662 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(
663 flags
.dark_room
? S_darkroom
: S_stone
));
664 else if (lev
->typ
== CORR
&& lev
->glyph
== cmap_to_glyph(S_litcorr
)
666 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
668 /* draw monster on top if we can sense it */
669 if ((x
!= u
.ux
|| y
!= u
.uy
) && (mon
= m_at(x
, y
)) && sensemon(mon
))
670 display_monster(x
, y
, mon
,
671 (tp_sensemon(mon
) || MATCH_WARN_OF_MON(mon
))
680 * Possibly put a new glyph at the given location.
686 register struct monst
*mon
;
687 register struct rm
*lev
= &(levl
[x
][y
]);
689 register xchar worm_tail
;
693 #ifdef HANGUPHANDLING
694 if (program_state
.done_hup
)
698 /* only permit updating the hero when swallowed */
700 if (x
== u
.ux
&& y
== u
.uy
)
704 if (Underwater
&& !Is_waterlevel(&u
.uz
)) {
705 /* when underwater, don't do anything unless <x,y> is an
706 adjacent water or lava or ice position */
707 if (!(is_pool_or_lava(x
, y
) || is_ice(x
, y
)) || distu(x
, y
) > 2)
711 /* Can physically see the location. */
713 NhRegion
*reg
= visible_region_at(x
, y
);
715 * Don't use templit here: E.g.
717 * lev->waslit = !!(lev->lit || templit(x,y));
719 * Otherwise we have the "light pool" problem, where non-permanently
720 * lit areas just out of sight stay remembered as lit. They should
723 * Perhaps ALL areas should revert to their "unlit" look when
726 lev
->waslit
= (lev
->lit
!= 0); /* remember lit condition */
728 /* normal region shown only on accessible positions, but poison clouds
729 * also shown above lava, pools and moats.
731 if (reg
!= NULL
&& (ACCESSIBLE(lev
->typ
)
732 || (reg
->glyph
== cmap_to_glyph(S_poisoncloud
)
733 && (lev
->typ
== LAVAPOOL
|| lev
->typ
== POOL
734 || lev
->typ
== MOAT
)))) {
735 show_region(reg
, x
, y
);
738 if (x
== u
.ux
&& y
== u
.uy
) {
740 _map_location(x
, y
, 0); /* map *under* self */
743 /* we can see what is there */
744 _map_location(x
, y
, 1);
747 worm_tail
= is_worm_tail(mon
);
749 mon
&& (worm_tail
? (!mon
->minvis
|| See_invisible
)
750 : (mon_visible(mon
)) || tp_sensemon(mon
)
751 || MATCH_WARN_OF_MON(mon
));
752 if (mon
&& (see_it
|| (!worm_tail
&& Detect_monsters
))) {
754 struct trap
*trap
= t_at(x
, y
);
755 int tt
= trap
? trap
->ttyp
: NO_TRAP
;
757 /* if monster is in a physical trap, you see the trap too
759 if (tt
== BEAR_TRAP
|| tt
== PIT
|| tt
== SPIKED_PIT
764 _map_location(x
, y
, 0); /* map under the monster */
765 /* also gets rid of any invisibility glyph */
766 display_monster(x
, y
, mon
,
767 see_it
? PHYSICALLY_SEEN
: DETECTED
,
769 } else if (mon
&& mon_warning(mon
) && !is_worm_tail(mon
)) {
770 display_warning(mon
);
771 } else if (glyph_is_invisible(levl
[x
][y
].glyph
)) {
774 _map_location(x
, y
, 1); /* map the location */\
777 /* Can't see the location. */
779 if (x
== u
.ux
&& y
== u
.uy
) {
780 feel_location(u
.ux
, u
.uy
); /* forces an update */
784 } else if ((mon
= m_at(x
, y
))
785 && ((see_it
= (tp_sensemon(mon
) || MATCH_WARN_OF_MON(mon
)
786 || (see_with_infrared(mon
)
787 && mon_visible(mon
))))
788 || Detect_monsters
)) {
789 /* Monsters are printed every time. */
790 /* This also gets rid of any invisibility glyph */
791 display_monster(x
, y
, mon
, see_it
? 0 : DETECTED
,
792 is_worm_tail(mon
) ? TRUE
: FALSE
);
793 } else if ((mon
= m_at(x
, y
)) && mon_warning(mon
)
794 && !is_worm_tail(mon
)) {
795 display_warning(mon
);
799 * If the location is remembered as being both dark (waslit is false)
800 * and lit (glyph is a lit room or lit corridor) then it was either:
802 * (1) A dark location that the hero could see through night
805 * (2) Darkened while out of the hero's sight. This can happen
806 * when cursed scroll of light is read.
808 * In either case, we have to manually correct the hero's memory to
809 * match waslit. Deciding when to change waslit is non-trivial.
811 * Note: If flags.lit_corridor is set, then corridors act like room
812 * squares. That is, they light up if in night vision range.
813 * If flags.lit_corridor is not set, then corridors will
814 * remain dark unless lit by a light spell and may darken
815 * again, as discussed above.
817 * These checks and changes must be here and not in back_to_glyph().
818 * They are dependent on the position being out of sight.
820 else if (Is_rogue_level(&u
.uz
)) {
821 if (lev
->glyph
== cmap_to_glyph(S_litcorr
) && lev
->typ
== CORR
)
822 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
823 else if (lev
->glyph
== cmap_to_glyph(S_room
) && lev
->typ
== ROOM
825 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_stone
));
829 else if (!lev
->waslit
|| (flags
.dark_room
&& iflags
.use_color
)) {
830 if (lev
->glyph
== cmap_to_glyph(S_litcorr
) && lev
->typ
== CORR
)
831 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(S_corr
));
832 else if (lev
->glyph
== cmap_to_glyph(S_room
) && lev
->typ
== ROOM
)
833 show_glyph(x
, y
, lev
->glyph
= cmap_to_glyph(DARKROOMSYM
));
838 show_glyph(x
, y
, lev
->glyph
);
848 * Put magic shield pyrotechnics at the given location. This *could* be
849 * pulled into a platform dependent routine for fancier graphics if desired.
859 if (cansee(x
, y
)) { /* Don't see anything if can't see the location */
860 for (i
= 0; i
< SHIELD_COUNT
; i
++) {
861 show_glyph(x
, y
, cmap_to_glyph(shield_static
[i
]));
862 flush_screen(1); /* make sure the glyph shows up */
865 newsym(x
, y
); /* restore the old information */
872 * Temporarily place glyphs on the screen. Do not call delay_output(). It
873 * is up to the caller to decide if it wants to wait [presently, everyone
874 * but explode() wants to delay].
877 * (DISP_BEAM, glyph) open, initialize glyph
878 * (DISP_FLASH, glyph) open, initialize glyph
879 * (DISP_ALWAYS, glyph) open, initialize glyph
880 * (DISP_CHANGE, glyph) change glyph
881 * (DISP_END, 0) close & clean up (second argument doesn't
883 * (DISP_FREEMEM, 0) only used to prevent memory leak during
885 * (x, y) display the glyph at the location
887 * DISP_BEAM - Display the given glyph at each location, but do not erase
888 * any until the close call.
889 * DISP_FLASH - Display the given glyph at each location, but erase the
890 * previous location's glyph.
891 * DISP_ALWAYS- Like DISP_FLASH, but vision is not taken into account.
894 #define TMP_AT_MAX_GLYPHS (COLNO * 2)
896 static struct tmp_glyph
{
897 coord saved
[TMP_AT_MAX_GLYPHS
]; /* previously updated positions */
898 int sidx
; /* index of next unused slot in saved[] */
899 int style
; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
900 int glyph
; /* glyph to use when printing */
901 struct tmp_glyph
*prev
;
908 static struct tmp_glyph
*tglyph
= (struct tmp_glyph
*) 0;
909 struct tmp_glyph
*tmp
;
918 else /* nested effect; we need dynamic memory */
919 tmp
= (struct tmp_glyph
*) alloc(sizeof(struct tmp_glyph
));
925 flush_screen(0); /* flush buffered glyphs */
928 case DISP_FREEMEM
: /* in case game ends with tmp_at() in progress */
931 if (tglyph
!= &tgfirst
)
932 free((genericptr_t
) tglyph
);
942 panic("tmp_at: tglyph not initialized");
950 if (tglyph
->style
== DISP_BEAM
|| tglyph
->style
== DISP_ALL
) {
953 /* Erase (reset) from source to end */
954 for (i
= 0; i
< tglyph
->sidx
; i
++)
955 newsym(tglyph
->saved
[i
].x
, tglyph
->saved
[i
].y
);
956 } else { /* DISP_FLASH or DISP_ALWAYS */
957 if (tglyph
->sidx
) /* been called at least once */
958 newsym(tglyph
->saved
[0].x
, tglyph
->saved
[0].y
);
960 /* tglyph->sidx = 0; -- about to be freed, so not necessary */
962 if (tglyph
!= &tgfirst
)
963 free((genericptr_t
) tglyph
);
970 if (tglyph
->style
== DISP_BEAM
|| tglyph
->style
== DISP_ALL
) {
971 if (tglyph
->style
!= DISP_ALL
&& !cansee(x
, y
))
973 if (tglyph
->sidx
>= TMP_AT_MAX_GLYPHS
)
974 break; /* too many locations */
975 /* save pos for later erasing */
976 tglyph
->saved
[tglyph
->sidx
].x
= x
;
977 tglyph
->saved
[tglyph
->sidx
].y
= y
;
979 } else { /* DISP_FLASH/ALWAYS */
980 if (tglyph
->sidx
) { /* not first call, so reset previous pos */
981 newsym(tglyph
->saved
[0].x
, tglyph
->saved
[0].y
);
982 tglyph
->sidx
= 0; /* display is presently up to date */
984 if (!cansee(x
, y
) && tglyph
->style
!= DISP_ALWAYS
)
986 tglyph
->saved
[0].x
= x
;
987 tglyph
->saved
[0].y
= y
;
991 show_glyph(x
, y
, tglyph
->glyph
); /* show it */
992 flush_screen(0); /* make sure it shows up */
1000 * The hero is swallowed. Show a special graphics sequence for this. This
1001 * bypasses all of the display routines and messes with buffered screen
1002 * directly. This method works because both vision and display check for
1009 static xchar lastx
, lasty
; /* last swallowed position */
1010 int swallower
, left_ok
, rght_ok
;
1018 /* Clear old location */
1019 for (y
= lasty
- 1; y
<= lasty
+ 1; y
++)
1020 for (x
= lastx
- 1; x
<= lastx
+ 1; x
++)
1022 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1025 swallower
= monsndx(u
.ustuck
->data
);
1026 /* assume isok(u.ux,u.uy) */
1027 left_ok
= isok(u
.ux
- 1, u
.uy
);
1028 rght_ok
= isok(u
.ux
+ 1, u
.uy
);
1030 * Display the hero surrounded by the monster's stomach.
1032 if (isok(u
.ux
, u
.uy
- 1)) {
1034 show_glyph(u
.ux
- 1, u
.uy
- 1,
1035 swallow_to_glyph(swallower
, S_sw_tl
));
1036 show_glyph(u
.ux
, u
.uy
- 1, swallow_to_glyph(swallower
, S_sw_tc
));
1038 show_glyph(u
.ux
+ 1, u
.uy
- 1,
1039 swallow_to_glyph(swallower
, S_sw_tr
));
1043 show_glyph(u
.ux
- 1, u
.uy
, swallow_to_glyph(swallower
, S_sw_ml
));
1046 show_glyph(u
.ux
+ 1, u
.uy
, swallow_to_glyph(swallower
, S_sw_mr
));
1048 if (isok(u
.ux
, u
.uy
+ 1)) {
1050 show_glyph(u
.ux
- 1, u
.uy
+ 1,
1051 swallow_to_glyph(swallower
, S_sw_bl
));
1052 show_glyph(u
.ux
, u
.uy
+ 1, swallow_to_glyph(swallower
, S_sw_bc
));
1054 show_glyph(u
.ux
+ 1, u
.uy
+ 1,
1055 swallow_to_glyph(swallower
, S_sw_br
));
1058 /* Update the swallowed position. */
1066 * Similar to swallowed() in operation. Shows hero when underwater
1067 * except when in water level. Special routines exist for that.
1073 static xchar lastx
, lasty
;
1074 static boolean dela
;
1077 /* swallowing has a higher precedence than under water */
1078 if (Is_waterlevel(&u
.uz
) || u
.uswallow
)
1082 if (mode
== 1 || dela
) {
1086 /* delayed full update */
1087 } else if (mode
== 2) {
1091 /* limited update */
1093 for (y
= lasty
- 1; y
<= lasty
+ 1; y
++)
1094 for (x
= lastx
- 1; x
<= lastx
+ 1; x
++)
1096 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1100 * TODO? Should this honor Xray radius rather than force radius 1?
1103 for (x
= u
.ux
- 1; x
<= u
.ux
+ 1; x
++)
1104 for (y
= u
.uy
- 1; y
<= u
.uy
+ 1; y
++)
1105 if (isok(x
, y
) && (is_pool_or_lava(x
, y
) || is_ice(x
, y
))) {
1106 if (Blind
&& !(x
== u
.ux
&& y
== u
.uy
))
1107 show_glyph(x
, y
, cmap_to_glyph(S_stone
));
1118 * Very restricted display. You can only see yourself.
1124 static boolean dela
;
1126 /* swallowing has a higher precedence than under ground */
1131 if (mode
== 1 || dela
) {
1135 /* delayed full update */
1136 } else if (mode
== 2) {
1140 /* limited update */
1146 /* =========================================================================
1150 * Loop through all of the monsters and update them. Called when:
1151 * + going blind & telepathic
1152 * + regaining sight & telepathic
1153 * + getting and losing infravision
1155 * + doing a full screen redraw
1156 * + see invisible times out or a ring of see invisible is taken off
1157 * + when a potion of see invisible is quaffed or a ring of see
1158 * invisible is put on
1159 * + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1160 * + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1166 register struct monst
*mon
;
1167 int new_warn_obj_cnt
= 0;
1169 if (defer_see_monsters
)
1172 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
1173 if (DEADMONSTER(mon
))
1175 newsym(mon
->mx
, mon
->my
);
1178 if (Warn_of_mon
&& (context
.warntype
.obj
& mon
->data
->mflags2
) != 0L)
1182 * Make Sting glow blue or stop glowing if required.
1184 if (new_warn_obj_cnt
!= warn_obj_cnt
) {
1185 Sting_effects(new_warn_obj_cnt
);
1186 warn_obj_cnt
= new_warn_obj_cnt
;
1189 /* when mounted, hero's location gets caught by monster loop */
1195 * Block/unblock light depending on what a mimic is mimicing and if it's
1196 * invisible or not. Should be called only when the state of See_invisible
1200 set_mimic_blocking()
1202 register struct monst
*mon
;
1204 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
1205 if (DEADMONSTER(mon
))
1207 if (mon
->minvis
&& is_lightblocker_mappear(mon
)) {
1209 block_point(mon
->mx
, mon
->my
);
1211 unblock_point(mon
->mx
, mon
->my
);
1217 * Loop through all of the object *locations* and update them. Called when
1223 register struct obj
*obj
;
1224 for (obj
= fobj
; obj
; obj
= obj
->nobj
)
1225 if (vobj_at(obj
->ox
, obj
->oy
) == obj
)
1226 newsym(obj
->ox
, obj
->oy
);
1230 * Update hallucinated traps.
1238 for (trap
= ftrap
; trap
; trap
= trap
->ntrap
) {
1239 glyph
= glyph_at(trap
->tx
, trap
->ty
);
1240 if (glyph_is_trap(glyph
))
1241 newsym(trap
->tx
, trap
->ty
);
1246 * Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1251 flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1265 register struct rm
*lev
;
1268 return; /* display isn't ready yet */
1274 if (Underwater
&& !Is_waterlevel(&u
.uz
)) {
1283 /* shut down vision */
1287 * This routine assumes that cls() does the following:
1288 * + fills the physical screen with the symbol for rock
1289 * + clears the glyph buffer
1293 /* display memory */
1294 for (x
= 1; x
< COLNO
; x
++) {
1296 for (y
= 0; y
< ROWNO
; y
++, lev
++)
1297 if (lev
->glyph
!= cmap_to_glyph(S_stone
))
1298 show_glyph(x
, y
, lev
->glyph
);
1301 /* see what is to be seen */
1304 /* overlay with monsters */
1307 context
.botlx
= 1; /* force a redraw of the bottom line */
1310 /* =========================================================================
1312 /* Glyph Buffering (3rd screen) ============================================
1316 xchar
new; /* perhaps move this bit into the rm structure. */
1320 static gbuf_entry gbuf
[ROWNO
][COLNO
];
1321 static char gbuf_start
[ROWNO
];
1322 static char gbuf_stop
[ROWNO
];
1324 /* FIXME: This is a dirty hack, because newsym() doesn't distinguish
1325 * between object piles and single objects, it doesn't mark the location
1333 if (gbuf_start
[y
] > x
)
1335 if (gbuf_stop
[y
] < x
)
1340 * Store the glyph in the 3rd screen for later flushing.
1343 show_glyph(x
, y
, glyph
)
1347 * Check for bad positions and glyphs.
1353 /* column 0 is invalid, but it's often used as a flag, so ignore it */
1358 * This assumes an ordering of the offsets. See display.h for
1362 if (glyph
>= GLYPH_WARNING_OFF
1363 && glyph
< GLYPH_STATUE_OFF
) { /* a warning */
1365 offset
= glyph
- GLYPH_WARNING_OFF
;
1366 } else if (glyph
>= GLYPH_SWALLOW_OFF
) { /* swallow border */
1367 text
= "swallow border";
1368 offset
= glyph
- GLYPH_SWALLOW_OFF
;
1369 } else if (glyph
>= GLYPH_ZAP_OFF
) { /* zap beam */
1371 offset
= glyph
- GLYPH_ZAP_OFF
;
1372 } else if (glyph
>= GLYPH_EXPLODE_OFF
) { /* explosion */
1374 offset
= glyph
- GLYPH_EXPLODE_OFF
;
1375 } else if (glyph
>= GLYPH_CMAP_OFF
) { /* cmap */
1376 text
= "cmap_index";
1377 offset
= glyph
- GLYPH_CMAP_OFF
;
1378 } else if (glyph
>= GLYPH_OBJ_OFF
) { /* object */
1380 offset
= glyph
- GLYPH_OBJ_OFF
;
1381 } else if (glyph
>= GLYPH_RIDDEN_OFF
) { /* ridden mon */
1382 text
= "ridden mon";
1383 offset
= glyph
- GLYPH_RIDDEN_OFF
;
1384 } else if (glyph
>= GLYPH_BODY_OFF
) { /* a corpse */
1386 offset
= glyph
- GLYPH_BODY_OFF
;
1387 } else if (glyph
>= GLYPH_DETECT_OFF
) { /* detected mon */
1388 text
= "detected mon";
1389 offset
= glyph
- GLYPH_DETECT_OFF
;
1390 } else if (glyph
>= GLYPH_INVIS_OFF
) { /* invisible mon */
1391 text
= "invisible mon";
1392 offset
= glyph
- GLYPH_INVIS_OFF
;
1393 } else if (glyph
>= GLYPH_PET_OFF
) { /* a pet */
1395 offset
= glyph
- GLYPH_PET_OFF
;
1396 } else { /* a monster */
1401 impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].", x
, y
,
1402 glyph
, text
, offset
);
1406 if (glyph
>= MAX_GLYPH
) {
1407 impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).", glyph
,
1412 if (gbuf
[y
][x
].glyph
!= glyph
|| iflags
.use_background_glyph
) {
1413 gbuf
[y
][x
].glyph
= glyph
;
1415 if (gbuf_start
[y
] > x
)
1417 if (gbuf_stop
[y
] < x
)
1423 * Reset the changed glyph borders so that none of the 3rd screen has
1426 #define reset_glyph_bbox() \
1430 for (i = 0; i < ROWNO; i++) { \
1431 gbuf_start[i] = COLNO - 1; \
1436 static gbuf_entry nul_gbuf
= { 0, cmap_to_glyph(S_stone
) };
1438 * Turn the 3rd screen into stone.
1441 clear_glyph_buffer()
1444 register gbuf_entry
*gptr
;
1446 for (y
= 0; y
< ROWNO
; y
++) {
1448 for (x
= COLNO
; x
; x
--) {
1456 * Assumes that the indicated positions are filled with S_stone glyphs.
1459 row_refresh(start
, stop
, y
)
1464 for (x
= start
; x
<= stop
; x
++)
1465 if (gbuf
[y
][x
].glyph
!= cmap_to_glyph(S_stone
))
1466 print_glyph(WIN_MAP
, x
, y
, gbuf
[y
][x
].glyph
, get_bk_glyph(x
,y
));
1472 static boolean in_cls
= 0;
1477 display_nhwindow(WIN_MESSAGE
, FALSE
); /* flush messages */
1478 context
.botlx
= 1; /* force update of botl window */
1479 clear_nhwindow(WIN_MAP
); /* clear physical screen */
1481 clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1486 * Synch the third screen with the display.
1489 flush_screen(cursor_on_u
)
1492 /* Prevent infinite loops on errors:
1493 * flush_screen->print_glyph->impossible->pline->flush_screen
1495 static boolean flushing
= 0;
1496 static boolean delay_flushing
= 0;
1499 if (cursor_on_u
== -1)
1500 delay_flushing
= !delay_flushing
;
1504 return; /* if already flushing then return */
1506 #ifdef HANGUPHANDLING
1507 if (program_state
.done_hup
)
1511 for (y
= 0; y
< ROWNO
; y
++) {
1512 register gbuf_entry
*gptr
= &gbuf
[y
][x
= gbuf_start
[y
]];
1513 for (; x
<= gbuf_stop
[y
]; gptr
++, x
++)
1515 print_glyph(WIN_MAP
, x
, y
, gptr
->glyph
, get_bk_glyph(x
, y
));
1521 curs(WIN_MAP
, u
.ux
, u
.uy
); /* move cursor to the hero */
1522 display_nhwindow(WIN_MAP
, FALSE
);
1525 if (context
.botl
|| context
.botlx
)
1529 /* =========================================================================
1535 * Use the information in the rm structure at the given position to create
1536 * a glyph of a background.
1538 * I had to add a field in the rm structure (horizontal) so that we knew
1539 * if open doors and secret doors were horizontal or vertical. Previously,
1540 * the screen symbol had the horizontal/vertical information set at
1541 * level generation time.
1543 * I used the 'ladder' field (really doormask) for deciding if stairwells
1544 * were up or down. I didn't want to check the upstairs and dnstairs
1552 struct rm
*ptr
= &(levl
[x
][y
]);
1557 idx
= level
.flags
.arboreal
? S_tree
: S_stone
;
1563 idx
= (ptr
->waslit
|| flags
.lit_corridor
) ? S_litcorr
: S_corr
;
1577 idx
= ptr
->seenv
? wall_angle(ptr
) : S_stone
;
1580 if (ptr
->doormask
) {
1581 if (ptr
->doormask
& D_BROKEN
)
1583 else if (ptr
->doormask
& D_ISOPEN
)
1584 idx
= (ptr
->horizontal
) ? S_hodoor
: S_vodoor
;
1585 else /* else is closed */
1586 idx
= (ptr
->horizontal
) ? S_hcdoor
: S_vcdoor
;
1601 idx
= (ptr
->ladder
& LA_DOWN
) ? S_dnstair
: S_upstair
;
1604 idx
= (ptr
->ladder
& LA_DOWN
) ? S_dnladder
: S_upladder
;
1637 idx
= (ptr
->horizontal
) ? S_hcdbridge
: S_vcdbridge
;
1640 switch (ptr
->drawbridgemask
& DB_UNDER
) {
1654 impossible("Strange db-under: %d",
1655 ptr
->drawbridgemask
& DB_UNDER
);
1656 idx
= S_room
; /* something is better than nothing */
1660 case DRAWBRIDGE_DOWN
:
1661 idx
= (ptr
->horizontal
) ? S_hodbridge
: S_vodbridge
;
1664 impossible("back_to_glyph: unknown level type [ = %d ]", ptr
->typ
);
1669 return cmap_to_glyph(idx
);
1673 * swallow_to_glyph()
1675 * Convert a monster number and a swallow location into the correct glyph.
1676 * If you don't want a patchwork monster while hallucinating, decide on
1677 * a random monster in swallowed() and don't use what_mon() here.
1680 swallow_to_glyph(mnum
, loc
)
1684 if (loc
< S_sw_tl
|| S_sw_br
< loc
) {
1685 impossible("swallow_to_glyph: bad swallow location");
1688 return ((int) (what_mon(mnum
) << 3) | (loc
- S_sw_tl
)) + GLYPH_SWALLOW_OFF
;
1694 * Change the given zap direction and beam type into a glyph. Each beam
1695 * type has four glyphs, one for each of the symbols below. The order of
1696 * the zap symbols [0-3] as defined in rm.h are:
1698 * | S_vbeam ( 0, 1) or ( 0,-1)
1699 * - S_hbeam ( 1, 0) or (-1, 0)
1700 * \ S_lslant ( 1, 1) or (-1,-1)
1701 * / S_rslant (-1, 1) or ( 1,-1)
1704 zapdir_to_glyph(dx
, dy
, beam_type
)
1705 register int dx
, dy
;
1708 if (beam_type
>= NUM_ZAP
) {
1709 impossible("zapdir_to_glyph: illegal beam type");
1712 dx
= (dx
== dy
) ? 2 : (dx
&& dy
) ? 3 : dx
? 1 : 0;
1714 return ((int) ((beam_type
<< 2) | dx
)) + GLYPH_ZAP_OFF
;
1718 * Utility routine for dowhatis() used to find out the glyph displayed at
1719 * the location. This isn't necessarily the same as the glyph in the levl
1720 * structure, so we must check the "third screen".
1726 if (x
< 0 || y
< 0 || x
>= COLNO
|| y
>= ROWNO
)
1727 return cmap_to_glyph(S_room
); /* XXX */
1728 return gbuf
[y
][x
].glyph
;
1732 * This will be used to get the glyph for the background so that
1733 * it can potentially be merged into graphical window ports
1734 * to improve the appearance of stuff on dark room
1735 * squares and the plane of air etc.
1737 * Until that is working correctly in the branch, however, for now
1738 * we just return NO_GLYPH as an indicator to ignore it.
1745 int idx
, bkglyph
= NO_GLYPH
;
1746 struct rm
*lev
= &levl
[x
][y
];
1748 if (iflags
.use_background_glyph
&& lev
->seenv
!= 0
1749 && gbuf
[y
][x
].glyph
!= cmap_to_glyph(S_stone
)) {
1753 idx
= level
.flags
.arboreal
? S_tree
: S_stone
;
1759 idx
= (lev
->waslit
|| flags
.lit_corridor
) ? S_litcorr
: S_corr
;
1785 if (!cansee(x
, y
) && (!lev
->waslit
|| flags
.dark_room
)) {
1786 /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
1787 if (lev
->typ
== CORR
&& idx
== S_litcorr
)
1789 else if (idx
== S_room
)
1790 idx
= (flags
.dark_room
&& iflags
.use_color
)
1791 ? DARKROOMSYM
: S_stone
;
1795 bkglyph
= cmap_to_glyph(idx
);
1800 /* -------------------------------------------------------------------------
1802 /* Wall Angle --------------------------------------------------------------
1807 static const char *FDECL(type_to_name
, (int));
1808 static void FDECL(error4
, (int, int, int, int, int, int));
1810 static int bad_count
[MAX_TYPE
]; /* count of positions flagged as bad */
1811 static const char *type_names
[MAX_TYPE
] = {
1812 "STONE", "VWALL", "HWALL", "TLCORNER", "TRCORNER", "BLCORNER", "BRCORNER",
1813 "CROSSWALL", "TUWALL", "TDWALL", "TLWALL", "TRWALL", "DBWALL", "TREE",
1814 "SDOOR", "SCORR", "POOL", "MOAT", "WATER", "DRAWBRIDGE_UP", "LAVAPOOL",
1815 "IRON_BARS", "DOOR", "CORR", "ROOM", "STAIRS", "LADDER", "FOUNTAIN",
1816 "THRONE", "SINK", "GRAVE", "ALTAR", "ICE", "DRAWBRIDGE_DOWN", "AIR",
1824 return (type
< 0 || type
>= MAX_TYPE
) ? "unknown" : type_names
[type
];
1828 error4(x
, y
, a
, b
, c
, dd
)
1829 int x
, y
, a
, b
, c
, dd
;
1831 pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1832 type_to_name(levl
[x
][y
].typ
), x
, y
,
1833 a
? "1" : "", b
? "2" : "", c
? "3" : "", dd
? "4" : "");
1834 bad_count
[levl
[x
][y
].typ
]++;
1836 #endif /* WA_VERBOSE */
1839 * Return 'which' if position is implies an unfinished exterior. Return
1840 * zero otherwise. Unfinished implies outer area is rock or a corridor.
1842 * Things that are ambiguous: lava
1845 check_pos(x
, y
, which
)
1851 type
= levl
[x
][y
].typ
;
1852 if (IS_ROCK(type
) || type
== CORR
|| type
== SCORR
)
1857 /* Return TRUE if more than one is non-zero. */
1861 more_than_one(x
, y
, a
, b
, c
)
1864 if ((a
&& (b
| c
)) || (b
&& (a
| c
)) || (c
&& (a
| b
))) {
1865 error4(x
, y
, a
, b
, c
, 0);
1871 #define more_than_one(x, y, a, b, c) \
1872 (((a) && ((b) | (c))) || ((b) && ((a) | (c))) || ((c) && ((a) | (b))))
1875 /* Return the wall mode for a T wall. */
1877 set_twall(x0
, y0
, x1
, y1
, x2
, y2
, x3
, y3
)
1878 int x0
, y0
; /* used #if WA_VERBOSE */
1879 int x1
, y1
, x2
, y2
, x3
, y3
;
1881 int wmode
, is_1
, is_2
, is_3
;
1886 is_1
= check_pos(x1
, y1
, WM_T_LONG
);
1887 is_2
= check_pos(x2
, y2
, WM_T_BL
);
1888 is_3
= check_pos(x3
, y3
, WM_T_BR
);
1889 if (more_than_one(x0
, y0
, is_1
, is_2
, is_3
)) {
1892 wmode
= is_1
+ is_2
+ is_3
;
1897 /* Return wall mode for a horizontal or vertical wall. */
1899 set_wall(x
, y
, horiz
)
1902 int wmode
, is_1
, is_2
;
1905 is_1
= check_pos(x
, y
- 1, WM_W_TOP
);
1906 is_2
= check_pos(x
, y
+ 1, WM_W_BOTTOM
);
1908 is_1
= check_pos(x
- 1, y
, WM_W_LEFT
);
1909 is_2
= check_pos(x
+ 1, y
, WM_W_RIGHT
);
1911 if (more_than_one(x
, y
, is_1
, is_2
, 0)) {
1914 wmode
= is_1
+ is_2
;
1919 /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
1921 set_corn(x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
)
1922 int x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1924 int wmode
, is_1
, is_2
, is_3
, is_4
;
1926 is_1
= check_pos(x1
, y1
, 1);
1927 is_2
= check_pos(x2
, y2
, 1);
1928 is_3
= check_pos(x3
, y3
, 1);
1929 is_4
= check_pos(x4
, y4
, 1); /* inner location */
1932 * All 4 should not be true. So if the inner location is rock,
1933 * use it. If all of the outer 3 are true, use outer. We currently
1934 * can't cover the case where only part of the outer is rock, so
1935 * we just say that all the walls are finished (if not overridden
1936 * by the inner section).
1940 } else if (is_1
&& is_2
&& is_3
)
1943 wmode
= 0; /* finished walls on all sides */
1948 /* Return mode for a crosswall. */
1953 int wmode
, is_1
, is_2
, is_3
, is_4
;
1955 is_1
= check_pos(x
- 1, y
- 1, 1);
1956 is_2
= check_pos(x
+ 1, y
- 1, 1);
1957 is_3
= check_pos(x
+ 1, y
+ 1, 1);
1958 is_4
= check_pos(x
- 1, y
+ 1, 1);
1960 wmode
= is_1
+ is_2
+ is_3
+ is_4
;
1962 if (is_1
&& is_3
&& (is_2
+ is_4
== 0)) {
1964 } else if (is_2
&& is_4
&& (is_1
+ is_3
== 0)) {
1968 error4(x
, y
, is_1
, is_2
, is_3
, is_4
);
1984 /* Called from mklev. Scan the level and set the wall modes. */
1993 for (x
= 0; x
< MAX_TYPE
; x
++)
1997 for (x
= 0; x
< COLNO
; x
++)
1998 for (lev
= &levl
[x
][0], y
= 0; y
< ROWNO
; y
++, lev
++) {
2001 wmode
= set_wall(x
, y
, (int) lev
->horizontal
);
2004 wmode
= set_wall(x
, y
, 0);
2007 wmode
= set_wall(x
, y
, 1);
2010 wmode
= set_twall(x
, y
, x
, y
- 1, x
- 1, y
+ 1, x
+ 1, 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
+ 1, y
, 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);
2023 set_corn(x
- 1, y
- 1, x
, y
- 1, x
- 1, y
, x
+ 1, y
+ 1);
2027 set_corn(x
, y
- 1, x
+ 1, y
- 1, x
+ 1, y
, x
- 1, y
+ 1);
2031 set_corn(x
, y
+ 1, x
- 1, y
+ 1, x
- 1, y
, x
+ 1, y
- 1);
2035 set_corn(x
+ 1, y
, x
+ 1, y
+ 1, x
, y
+ 1, x
- 1, y
- 1);
2038 wmode
= set_crosswall(x
, y
);
2042 wmode
= -1; /* don't set wall info */
2047 lev
->wall_info
= (lev
->wall_info
& ~WM_MASK
) | wmode
;
2051 /* check if any bad positions found */
2052 for (x
= y
= 0; x
< MAX_TYPE
; x
++)
2055 y
= 1; /* only print once */
2056 pline("set_wall_type: wall mode problems with: ");
2058 pline("%s %d;", type_names
[x
], bad_count
[x
]);
2060 #endif /* WA_VERBOSE */
2063 /* -------------------------------------------------------------------------
2065 /* This matrix is used here and in vision.c. */
2066 unsigned char seenv_matrix
[3][3] = { { SV2
, SV1
, SV0
},
2067 { SV3
, SVALL
, SV7
},
2068 { SV4
, SV5
, SV6
} };
2070 #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
2072 /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
2074 set_seenv(lev
, x0
, y0
, x
, y
)
2076 int x0
, y0
, x
, y
; /* from, to */
2078 int dx
= x
- x0
, dy
= y0
- y
;
2080 lev
->seenv
|= seenv_matrix
[sign(dy
) + 1][sign(dx
) + 1];
2083 /* Called by blackout(vault.c) when vault guard removes temporary corridor,
2084 turning spot <x0,y0> back into stone; <x1,y1> is an adjacent spot. */
2086 unset_seenv(lev
, x0
, y0
, x1
, y1
)
2087 struct rm
*lev
; /* &levl[x1][y1] */
2088 int x0
, y0
, x1
, y1
; /* from, to; abs(x1-x0)==1 && abs(y0-y1)==1 */
2090 int dx
= x1
- x0
, dy
= y0
- y1
;
2092 lev
->seenv
&= ~seenv_matrix
[dy
+ 1][dx
+ 1];
2095 /* -------------------------------------------------------------------------
2098 /* T wall types, one for each row in wall_matrix[][]. */
2105 * These are the column names of wall_matrix[][]. They are the "results"
2106 * of a tdwall pattern match. All T walls are rotated so they become
2107 * a tdwall. Then we do a single pattern match, but return the
2108 * correct result for the original wall by using different rows for
2109 * each of the wall types.
2117 static const int wall_matrix
[4][5] = {
2118 { S_stone
, S_tlcorn
, S_trcorn
, S_hwall
, S_tdwall
}, /* tdwall */
2119 { S_stone
, S_trcorn
, S_brcorn
, S_vwall
, S_tlwall
}, /* tlwall */
2120 { S_stone
, S_brcorn
, S_blcorn
, S_hwall
, S_tuwall
}, /* tuwall */
2121 { S_stone
, S_blcorn
, S_tlcorn
, S_vwall
, S_trwall
}, /* trwall */
2124 /* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][].
2132 * These are the column names for cross_matrix[][]. They express results
2133 * in C_br (bottom right) terms. All crosswalls with a single solid
2134 * quarter are rotated so the solid section is at the bottom right.
2135 * We pattern match on that, but return the correct result depending
2136 * on which row we'ere looking at.
2145 static const int cross_matrix
[4][6] = {
2146 { S_brcorn
, S_blcorn
, S_tlcorn
, S_tuwall
, S_trwall
, S_crwall
},
2147 { S_blcorn
, S_tlcorn
, S_trcorn
, S_trwall
, S_tdwall
, S_crwall
},
2148 { S_tlcorn
, S_trcorn
, S_brcorn
, S_tdwall
, S_tlwall
, S_crwall
},
2149 { S_trcorn
, S_brcorn
, S_blcorn
, S_tlwall
, S_tuwall
, S_crwall
},
2152 /* Print out a T wall warning and all interesting info. */
2157 static const char warn_str
[] = "wall_angle: %s: case %d: seenv = 0x%x";
2160 if (lev
->typ
== TUWALL
)
2162 else if (lev
->typ
== TLWALL
)
2164 else if (lev
->typ
== TRWALL
)
2166 else if (lev
->typ
== TDWALL
)
2170 impossible(warn_str
, wname
, lev
->wall_info
& WM_MASK
,
2171 (unsigned int) lev
->seenv
);
2175 * Return the correct graphics character index using wall type, wall mode,
2176 * and the seen vector. It is expected that seenv is non zero.
2178 * All T-wall vectors are rotated to be TDWALL. All single crosswall
2179 * blocks are rotated to bottom right. All double crosswall are rotated
2180 * to W_X_BLTR. All results are converted back.
2182 * The only way to understand this is to take out pen and paper and
2183 * draw diagrams. See rm.h for more details on the wall modes and
2190 register unsigned int seenv
= lev
->seenv
& 0xff;
2194 #define only(sv, bits) (((sv) & (bits)) && !((sv) & ~(bits)))
2197 row
= wall_matrix
[T_u
];
2198 seenv
= (seenv
>> 4 | seenv
<< 4) & 0xff; /* rotate to tdwall */
2201 row
= wall_matrix
[T_l
];
2202 seenv
= (seenv
>> 2 | seenv
<< 6) & 0xff; /* rotate to tdwall */
2205 row
= wall_matrix
[T_r
];
2206 seenv
= (seenv
>> 6 | seenv
<< 2) & 0xff; /* rotate to tdwall */
2209 row
= wall_matrix
[T_d
];
2211 switch (lev
->wall_info
& WM_MASK
) {
2215 } else if (seenv
== SV6
) {
2217 } else if (seenv
& (SV3
| SV5
| SV7
)
2218 || ((seenv
& SV4
) && (seenv
& SV6
))) {
2220 } else if (seenv
& (SV0
| SV1
| SV2
)) {
2221 col
= (seenv
& (SV4
| SV6
) ? T_tdwall
: T_hwall
);
2228 if (seenv
& (SV3
| SV4
) && !(seenv
& (SV5
| SV6
| SV7
))) {
2230 } else if (seenv
& (SV6
| SV7
) && !(seenv
& (SV3
| SV4
| SV5
))) {
2232 } else if ((seenv
& SV5
)
2233 || ((seenv
& (SV3
| SV4
)) && (seenv
& (SV6
| SV7
)))) {
2236 /* only SV0|SV1|SV2 */
2237 if (!only(seenv
, SV0
| SV1
| SV2
))
2243 #if 0 /* older method, fixed */
2244 if (only(seenv
, SV4
|SV5
)) {
2246 } else if ((seenv
& (SV0
|SV1
|SV2
))
2247 && only(seenv
, SV0
|SV1
|SV2
|SV6
|SV7
)) {
2249 } else if ((seenv
& SV3
)
2250 || ((seenv
& (SV0
|SV1
|SV2
)) && (seenv
& (SV4
|SV5
)))) {
2258 if (only(seenv
, SV4
| SV5
))
2260 else if ((seenv
& (SV0
| SV1
| SV2
| SV7
))
2261 && !(seenv
& (SV3
| SV4
| SV5
)))
2263 else if (only(seenv
, SV6
))
2269 #if 0 /* older method, fixed */
2270 if (only(seenv
, SV5
|SV6
)) {
2272 } else if ((seenv
& (SV0
|SV1
|SV2
))
2273 && only(seenv
, SV0
|SV1
|SV2
|SV3
|SV4
)) {
2275 } else if ((seenv
& SV7
)
2276 || ((seenv
& (SV0
|SV1
|SV2
)) && (seenv
& (SV5
|SV6
)))) {
2284 if (only(seenv
, SV5
| SV6
))
2286 else if ((seenv
& (SV0
| SV1
| SV2
| SV3
))
2287 && !(seenv
& (SV5
| SV6
| SV7
)))
2289 else if (only(seenv
, SV4
))
2296 impossible("wall_angle: unknown T wall mode %d",
2297 lev
->wall_info
& WM_MASK
);
2305 if (lev
->horizontal
)
2309 switch (lev
->wall_info
& WM_MASK
) {
2311 idx
= seenv
? S_vwall
: S_stone
;
2314 idx
= seenv
& (SV1
| SV2
| SV3
| SV4
| SV5
) ? S_vwall
: S_stone
;
2317 idx
= seenv
& (SV0
| SV1
| SV5
| SV6
| SV7
) ? S_vwall
: S_stone
;
2320 impossible("wall_angle: unknown vwall mode %d",
2321 lev
->wall_info
& WM_MASK
);
2329 switch (lev
->wall_info
& WM_MASK
) {
2331 idx
= seenv
? S_hwall
: S_stone
;
2334 idx
= seenv
& (SV3
| SV4
| SV5
| SV6
| SV7
) ? S_hwall
: S_stone
;
2337 idx
= seenv
& (SV0
| SV1
| SV2
| SV3
| SV7
) ? S_hwall
: S_stone
;
2340 impossible("wall_angle: unknown hwall mode %d",
2341 lev
->wall_info
& WM_MASK
);
2347 #define set_corner(idx, lev, which, outer, inner, name) \
2348 switch ((lev)->wall_info & WM_MASK) { \
2353 idx = seenv & (outer) ? which : S_stone; \
2356 idx = seenv & ~(inner) ? which : S_stone; \
2359 impossible("wall_angle: unknown %s mode %d", name, \
2360 (lev)->wall_info &WM_MASK); \
2366 set_corner(idx
, lev
, S_tlcorn
, (SV3
| SV4
| SV5
), SV4
, "tlcorn");
2369 set_corner(idx
, lev
, S_trcorn
, (SV5
| SV6
| SV7
), SV6
, "trcorn");
2372 set_corner(idx
, lev
, S_blcorn
, (SV1
| SV2
| SV3
), SV2
, "blcorn");
2375 set_corner(idx
, lev
, S_brcorn
, (SV7
| SV0
| SV1
), SV0
, "brcorn");
2379 switch (lev
->wall_info
& WM_MASK
) {
2383 else if (seenv
== SV2
)
2385 else if (seenv
== SV4
)
2387 else if (seenv
== SV6
)
2389 else if (!(seenv
& ~(SV0
| SV1
| SV2
))
2390 && (seenv
& SV1
|| seenv
== (SV0
| SV2
)))
2392 else if (!(seenv
& ~(SV2
| SV3
| SV4
))
2393 && (seenv
& SV3
|| seenv
== (SV2
| SV4
)))
2395 else if (!(seenv
& ~(SV4
| SV5
| SV6
))
2396 && (seenv
& SV5
|| seenv
== (SV4
| SV6
)))
2398 else if (!(seenv
& ~(SV0
| SV6
| SV7
))
2399 && (seenv
& SV7
|| seenv
== (SV0
| SV6
)))
2406 row
= cross_matrix
[C_tl
];
2407 seenv
= (seenv
>> 4 | seenv
<< 4) & 0xff;
2410 row
= cross_matrix
[C_tr
];
2411 seenv
= (seenv
>> 6 | seenv
<< 2) & 0xff;
2414 row
= cross_matrix
[C_bl
];
2415 seenv
= (seenv
>> 2 | seenv
<< 6) & 0xff;
2418 row
= cross_matrix
[C_br
];
2423 seenv
= seenv
& ~SV4
; /* strip SV4 */
2426 } else if (seenv
& (SV2
| SV3
)) {
2427 if (seenv
& (SV5
| SV6
| SV7
))
2429 else if (seenv
& (SV0
| SV1
))
2433 } else if (seenv
& (SV5
| SV6
)) {
2434 if (seenv
& (SV1
| SV2
| SV3
))
2436 else if (seenv
& (SV0
| SV7
))
2440 } else if (seenv
& SV1
) {
2441 col
= seenv
& SV7
? C_crwall
: C_tuwall
;
2442 } else if (seenv
& SV7
) {
2443 col
= seenv
& SV1
? C_crwall
: C_tlwall
;
2445 impossible("wall_angle: bottom of crwall check");
2454 if (only(seenv
, SV1
| SV2
| SV3
))
2456 else if (only(seenv
, SV5
| SV6
| SV7
))
2458 else if (only(seenv
, SV0
| SV4
))
2465 if (only(seenv
, SV0
| SV1
| SV7
))
2467 else if (only(seenv
, SV3
| SV4
| SV5
))
2469 else if (only(seenv
, SV2
| SV6
))
2476 impossible("wall_angle: unknown crosswall mode");
2483 impossible("wall_angle: unexpected wall type %d", lev
->typ
);