1
/* SCCS Id: @(#)display.c 3.4 2003/02/19 */
2
/* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */
3
/* and Dave Cohrs, 1990. */
4
/* NetHack may be freely redistributed. See license for details. */
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 a wall from the other.
110
* Other uses are for unmapping detected objects and felt
111
* locations, where we need to know if the hero has ever
113
* flags - Additional information for the typ field. Different for
115
* horizontal - Indicates whether the wall or door is horizontal or
121
STATIC_DCL void FDECL(display_monster,(XCHAR_P,XCHAR_P,struct monst *,int,XCHAR_P));
122
STATIC_DCL int FDECL(swallow_to_glyph, (int, int));
123
STATIC_DCL void FDECL(display_warning,(struct monst *));
125
STATIC_DCL int FDECL(check_pos, (int, int, int));
127
STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
129
STATIC_DCL int FDECL(set_twall, (int,int, int,int, int,int, int,int));
130
STATIC_DCL int FDECL(set_wall, (int, int, int));
131
STATIC_DCL int FDECL(set_corn, (int,int, int,int, int,int, int,int));
132
STATIC_DCL int FDECL(set_crosswall, (int, int));
133
STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int));
134
STATIC_DCL void FDECL(t_warn, (struct rm *));
135
STATIC_DCL int FDECL(wall_angle, (struct rm *));
137
#ifdef INVISIBLE_OBJECTS
141
* Returns a pointer to an object if the hero can see an object at the
142
* given location. This takes care of invisible objects. NOTE, this
143
* assumes that the hero is not blind and on top of the object pile.
144
* It does NOT take into account that the location is out of sight, or,
145
* say, one can see blessed, etc.
151
register struct obj *obj = level.objects[x][y];
154
if (!obj->oinvis || See_invisible) return obj;
157
return ((struct obj *) 0);
159
#endif /* else vobj_at() is defined in display.h */
162
* magic_map_background()
164
* This function is similar to map_background (see below) except we pay
165
* attention to and correct unexplored, lit ROOM and CORR spots.
168
magic_map_background(x, y, show)
172
int glyph = back_to_glyph(x,y); /* assumes hero can see x,y */
173
struct rm *lev = &levl[x][y];
176
* Correct for out of sight lit corridors and rooms that the hero
177
* doesn't remember as lit.
179
if (!cansee(x,y) && !lev->waslit) {
180
/* Floor spaces are dark if unlit. Corridors are dark if unlit. */
181
if (lev->typ == ROOM && glyph == cmap_to_glyph(S_room))
182
glyph = cmap_to_glyph(S_stone);
183
else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
184
glyph = cmap_to_glyph(S_corr);
186
if (level.flags.hero_memory)
188
if (show) show_glyph(x,y, glyph);
192
* The routines map_background(), map_object(), and map_trap() could just
195
* map_glyph(x,y,glyph,show)
197
* Which is called with the xx_to_glyph() in the call. Then I can get
198
* rid of 3 routines that don't do very much anyway. And then stop
199
* having to create fake objects and traps. However, I am reluctant to
202
/* FIXME: some of these use xchars for x and y, and some use ints. Make
209
* Make the real background part of our map. This routine assumes that
210
* the hero can physically see the location. Update the screen if directed.
213
map_background(x, y, show)
217
register int glyph = back_to_glyph(x,y);
219
if (level.flags.hero_memory)
220
levl[x][y].glyph = glyph;
221
if (show) show_glyph(x,y, glyph);
227
* Map the trap and print it out if directed. This routine assumes that the
228
* hero can physically see the location.
232
register struct trap *trap;
235
register int x = trap->tx, y = trap->ty;
236
register int glyph = trap_to_glyph(trap);
238
if (level.flags.hero_memory)
239
levl[x][y].glyph = glyph;
240
if (show) show_glyph(x, y, glyph);
246
* Map the given object. This routine assumes that the hero can physically
247
* see the location of the object. Update the screen if directed.
250
map_object(obj, show)
251
register struct obj *obj;
254
register int x = obj->ox, y = obj->oy;
255
register int glyph = obj_to_glyph(obj);
257
if (level.flags.hero_memory)
258
levl[x][y].glyph = glyph;
259
if (show) show_glyph(x, y, glyph);
265
* Make the hero remember that a square contains an invisible monster.
266
* This is a special case in that the square will continue to be displayed
267
* this way even when the hero is close enough to see it. To get rid of
268
* this and display the square's actual contents, use unmap_object() followed
269
* by newsym() if necessary.
275
if (x != u.ux || y != u.uy) { /* don't display I at hero's location */
276
if (level.flags.hero_memory)
277
levl[x][y].glyph = GLYPH_INVISIBLE;
278
show_glyph(x, y, GLYPH_INVISIBLE);
285
* Remove something from the map when the hero realizes it's not there any
286
* more. Replace it with background or known trap, but not with any other
287
* If this is used for detection, a full screen update is imminent anyway;
288
* if this is used to get rid of an invisible monster notation, we might have
295
register struct trap *trap;
297
if (!level.flags.hero_memory) return;
299
if ((trap = t_at(x,y)) != 0 && trap->tseen && !covers_traps(x,y))
301
else if (levl[x][y].seenv) {
302
struct rm *lev = &levl[x][y];
304
map_background(x, y, 0);
306
/* turn remembered dark room squares dark */
307
if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) &&
309
lev->glyph = cmap_to_glyph(S_stone);
311
levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
318
* Make whatever at this location show up. This is only for non-living
319
* things. This will not handle feeling invisible objects correctly.
321
* Internal to display.c, this is a #define for speed.
323
#define _map_location(x,y,show) \
325
register struct obj *obj; \
326
register struct trap *trap; \
328
if ((obj = vobj_at(x,y)) && !covers_objects(x,y)) \
329
map_object(obj,show); \
330
else if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y)) \
331
map_trap(trap,show); \
333
map_background(x,y,show); \
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
register xchar worm_tail; /* mon is actually a worm tail */
366
register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING);
367
register int sensed = mon_mimic &&
368
(Protection_from_shape_changers || sensemon(mon));
370
* We must do the mimic check first. If the mimic is mimicing something,
371
* and the location is in sight, we have to change the hero's memory
372
* so that when the position is out of sight, the hero remembers what
373
* the mimic was mimicing.
376
if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) {
377
switch (mon->m_ap_type) {
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
register int glyph = cmap_to_glyph(mon->mappearance);
396
levl[x][y].glyph = glyph;
397
if (!sensed) show_glyph(x,y, glyph);
402
struct obj obj; /* Make a fake object to send */
403
/* to map_object(). */
406
obj.otyp = mon->mappearance;
407
obj.corpsenm = PM_TENGU; /* if mimicing a corpse */
408
map_object(&obj,!sensed);
413
show_glyph(x,y, monnum_to_glyph(what_mon((int)mon->mappearance)));
419
/* If the mimic is unsucessfully mimicing something, display the monster */
420
if (!mon_mimic || sensed) {
423
/* [ALI] Only use detected glyphs when monster wouldn't be
424
* visible by any other means.
426
if (sightflags == DETECTED) {
428
num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
430
num = detected_mon_to_glyph(mon);
431
} else if (mon->mtame && !Hallucination) {
433
num = petnum_to_glyph(PM_LONG_WORM_TAIL);
435
num = pet_to_glyph(mon);
438
num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
440
num = mon_to_glyph(mon);
449
* This is also *not* a map_XXXX() function! Monster warnings float
450
* above everything just like monsters do, but only if the monster
453
* Do not call for worm tails.
457
register struct monst *mon;
459
int x = mon->mx, y = mon->my;
460
int wl = (int) (mon->m_lev / 4);
463
if (mon_warning(mon)) {
464
if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1;
465
/* 3.4.1: this really ought to be rn2(WARNCOUNT), but value "0"
466
isn't handled correctly by the what_is routine so avoid it */
467
if (Hallucination) wl = rn1(WARNCOUNT-1,1);
468
glyph = warning_to_glyph(wl);
469
} else if (MATCH_WARN_OF_MON(mon)) {
470
glyph = mon_to_glyph(mon);
472
impossible("display_warning did not match warning type?");
475
show_glyph(x, y, glyph);
481
* Feel the given location. This assumes that the hero is blind and that
482
* the given position is either the hero's or one of the eight squares
483
* adjacent to the hero (except for a boulder push).
484
* If an invisible monster has gone away, that will be discovered. If an
485
* invisible monster has appeared, this will _not_ be discovered since
486
* searching only finds one monster per turn so we must check that separately.
492
struct rm *lev = &(levl[x][y]);
494
register struct monst *mon;
496
/* If the hero's memory of an invisible monster is accurate, we want to keep
497
* him from detecting the same monster over and over again on each turn.
498
* We must return (so we don't erase the monster). (We must also, in the
499
* search function, be sure to skip over previously detected 'I's.)
501
if (glyph_is_invisible(levl[x][y].glyph) && m_at(x,y)) return;
503
/* The hero can't feel non pool locations while under water. */
504
if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(x,y))
507
/* Set the seen vector as if the hero had seen it. It doesn't matter */
508
/* if the hero is levitating or not. */
509
set_seenv(lev, u.ux, u.uy, x, y);
511
if (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) {
513
* Levitation Rules. It is assumed that the hero can feel the state
514
* of the walls around herself and can tell if she is in a corridor,
515
* room, or doorway. Boulders are felt because they are large enough.
516
* Anything else is unknown because the hero can't reach the ground.
517
* This makes things difficult.
519
* Check (and display) in order:
521
* + Stone, walls, and closed doors.
522
* + Boulders. [see a boulder before a doorway]
524
* + Room/water positions
525
* + Everything else (hallways!)
527
if (IS_ROCK(lev->typ) || (IS_DOOR(lev->typ) &&
528
(lev->doormask & (D_LOCKED | D_CLOSED)))) {
529
map_background(x, y, 1);
530
} else if ((boulder = sobj_at(BOULDER,x,y)) != 0) {
531
map_object(boulder, 1);
532
} else if (IS_DOOR(lev->typ)) {
533
map_background(x, y, 1);
534
} else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
536
* An open room or water location. Normally we wouldn't touch
537
* this, but we have to get rid of remembered boulder symbols.
538
* This will only occur in rare occations when the hero goes
539
* blind and doesn't find a boulder where expected (something
540
* came along and picked it up). We know that there is not a
541
* boulder at this location. Show fountains, pools, etc.
542
* underneath if already seen. Otherwise, show the appropriate
545
* Similarly, if the hero digs a hole in a wall or feels a location
546
* that used to contain an unseen monster. In these cases,
547
* there's no reason to assume anything was underneath, so
548
* just show the appropriate floor symbol. If something was
549
* embedded in the wall, the glyph will probably already
550
* reflect that. Don't change the symbol in this case.
552
* This isn't quite correct. If the boulder was on top of some
553
* other objects they should be seen once the boulder is removed.
554
* However, we have no way of knowing that what is there now
555
* was there then. So we let the hero have a lapse of memory.
556
* We could also just display what is currently on the top of the
557
* object stack (if anything).
559
if (lev->glyph == objnum_to_glyph(BOULDER)) {
560
if (lev->typ != ROOM && lev->seenv) {
561
map_background(x, y, 1);
563
lev->glyph = lev->waslit ? cmap_to_glyph(S_room) :
564
cmap_to_glyph(S_stone);
565
show_glyph(x,y,lev->glyph);
567
} else if ((lev->glyph >= cmap_to_glyph(S_stone) &&
568
lev->glyph < cmap_to_glyph(S_room)) ||
569
glyph_is_invisible(levl[x][y].glyph)) {
570
lev->glyph = lev->waslit ? cmap_to_glyph(S_room) :
571
cmap_to_glyph(S_stone);
572
show_glyph(x,y,lev->glyph);
575
/* We feel it (I think hallways are the only things left). */
576
map_background(x, y, 1);
577
/* Corridors are never felt as lit (unless remembered that way) */
578
/* (lit_corridor only). */
579
if (lev->typ == CORR &&
580
lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit)
581
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
584
_map_location(x, y, 1);
588
* A ball or chain is only felt if it is first on the object
589
* location list. Otherwise, we need to clear the felt bit ---
590
* something has been dropped on the ball/chain. If the bit is
591
* not cleared, then when the ball/chain is moved it will drop
594
if (uchain->ox == x && uchain->oy == y) {
595
if (level.objects[x][y] == uchain)
596
u.bc_felt |= BC_CHAIN;
598
u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
600
if (!carried(uball) && uball->ox == x && uball->oy == y) {
601
if (level.objects[x][y] == uball)
602
u.bc_felt |= BC_BALL;
604
u.bc_felt &= ~BC_BALL; /* do not feel the ball */
608
/* Floor spaces are dark if unlit. Corridors are dark if unlit. */
609
if (lev->typ == ROOM &&
610
lev->glyph == cmap_to_glyph(S_room) && !lev->waslit)
611
show_glyph(x,y, lev->glyph = cmap_to_glyph(S_stone));
612
else if (lev->typ == CORR &&
613
lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit)
614
show_glyph(x,y, lev->glyph = cmap_to_glyph(S_corr));
616
/* draw monster on top if we can sense it */
617
if ((x != u.ux || y != u.uy) && (mon = m_at(x,y)) && sensemon(mon))
618
display_monster(x, y, mon,
619
(tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)) ? PHYSICALLY_SEEN : DETECTED,
626
* Possibly put a new glyph at the given location.
632
register struct monst *mon;
633
register struct rm *lev = &(levl[x][y]);
635
register xchar worm_tail;
637
if (in_mklev) return;
639
/* only permit updating the hero when swallowed */
641
if (x == u.ux && y == u.uy) display_self();
644
if (Underwater && !Is_waterlevel(&u.uz)) {
645
/* don't do anything unless (x,y) is an adjacent underwater position */
647
if (!is_pool(x,y)) return;
648
dx = x - u.ux; if (dx < 0) dx = -dx;
649
dy = y - u.uy; if (dy < 0) dy = -dy;
650
if (dx > 1 || dy > 1) return;
653
/* Can physically see the location. */
655
NhRegion* reg = visible_region_at(x,y);
657
* Don't use templit here: E.g.
659
* lev->waslit = !!(lev->lit || templit(x,y));
661
* Otherwise we have the "light pool" problem, where non-permanently
662
* lit areas just out of sight stay remembered as lit. They should
665
* Perhaps ALL areas should revert to their "unlit" look when
668
lev->waslit = (lev->lit!=0); /* remember lit condition */
670
if (reg != NULL && ACCESSIBLE(lev->typ)) {
671
show_region(reg,x,y);
674
if (x == u.ux && y == u.uy) {
676
_map_location(x,y,0); /* map *under* self */
679
/* we can see what is there */
680
_map_location(x,y,1);
684
worm_tail = is_worm_tail(mon);
685
see_it = mon && (worm_tail
686
? (!mon->minvis || See_invisible)
687
: (mon_visible(mon)) || tp_sensemon(mon) || MATCH_WARN_OF_MON(mon));
688
if (mon && (see_it || (!worm_tail && Detect_monsters))) {
690
struct trap *trap = t_at(x, y);
691
int tt = trap ? trap->ttyp : NO_TRAP;
693
/* if monster is in a physical trap, you see the trap too */
694
if (tt == BEAR_TRAP || tt == PIT ||
695
tt == SPIKED_PIT ||tt == WEB) {
699
_map_location(x,y,0); /* map under the monster */
700
/* also gets rid of any invisibility glyph */
701
display_monster(x, y, mon, see_it ? PHYSICALLY_SEEN : DETECTED, worm_tail);
703
else if (mon && mon_warning(mon) && !is_worm_tail(mon))
704
display_warning(mon);
705
else if (glyph_is_invisible(levl[x][y].glyph))
708
_map_location(x,y,1); /* map the location */
712
/* Can't see the location. */
714
if (x == u.ux && y == u.uy) {
715
feel_location(u.ux, u.uy); /* forces an update */
717
if (senseself()) display_self();
719
else if ((mon = m_at(x,y))
720
&& ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)
721
|| (see_with_infrared(mon) && mon_visible(mon))))
723
&& !is_worm_tail(mon)) {
724
/* Monsters are printed every time. */
725
/* This also gets rid of any invisibility glyph */
726
display_monster(x, y, mon, see_it ? 0 : DETECTED, 0);
728
else if ((mon = m_at(x,y)) && mon_warning(mon) &&
729
!is_worm_tail(mon)) {
730
display_warning(mon);
734
* If the location is remembered as being both dark (waslit is false)
735
* and lit (glyph is a lit room or lit corridor) then it was either:
737
* (1) A dark location that the hero could see through night
740
* (2) Darkened while out of the hero's sight. This can happen
741
* when cursed scroll of light is read.
743
* In either case, we have to manually correct the hero's memory to
744
* match waslit. Deciding when to change waslit is non-trivial.
746
* Note: If flags.lit_corridor is set, then corridors act like room
747
* squares. That is, they light up if in night vision range.
748
* If flags.lit_corridor is not set, then corridors will
749
* remain dark unless lit by a light spell and may darken
750
* again, as discussed above.
752
* These checks and changes must be here and not in back_to_glyph().
753
* They are dependent on the position being out of sight.
755
else if (!lev->waslit) {
756
if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR)
757
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
758
else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM)
759
show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone));
764
show_glyph(x, y, lev->glyph);
774
* Put magic shield pyrotechnics at the given location. This *could* be
775
* pulled into a platform dependent routine for fancier graphics if desired.
783
if (!flags.sparkle) return;
784
if (cansee(x,y)) { /* Don't see anything if can't see the location */
785
for (i = 0; i < SHIELD_COUNT; i++) {
786
show_glyph(x, y, cmap_to_glyph(shield_static[i]));
787
flush_screen(1); /* make sure the glyph shows up */
790
newsym(x,y); /* restore the old information */
798
* Temporarily place glyphs on the screen. Do not call delay_output(). It
799
* is up to the caller to decide if it wants to wait [presently, everyone
800
* but explode() wants to delay].
803
* (DISP_BEAM, glyph) open, initialize glyph
804
* (DISP_FLASH, glyph) open, initialize glyph
805
* (DISP_ALWAYS, glyph) open, initialize glyph
806
* (DISP_CHANGE, glyph) change glyph
807
* (DISP_END, 0) close & clean up (second argument doesn't
809
* (DISP_FREEMEM, 0) only used to prevent memory leak during
811
* (x, y) display the glyph at the location
813
* DISP_BEAM - Display the given glyph at each location, but do not erase
814
* any until the close call.
815
* DISP_FLASH - Display the given glyph at each location, but erase the
816
* previous location's glyph.
817
* DISP_ALWAYS- Like DISP_FLASH, but vision is not taken into account.
820
static struct tmp_glyph {
821
coord saved[COLNO]; /* previously updated positions */
822
int sidx; /* index of next unused slot in saved[] */
823
int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
824
int glyph; /* glyph to use when printing */
825
struct tmp_glyph *prev;
832
static struct tmp_glyph *tglyph = (struct tmp_glyph *)0;
833
struct tmp_glyph *tmp;
841
else /* nested effect; we need dynamic memory */
842
tmp = (struct tmp_glyph *)alloc(sizeof (struct tmp_glyph));
848
flush_screen(0); /* flush buffered glyphs */
851
case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
854
if (tglyph != &tgfirst) free((genericptr_t)tglyph);
863
if (!tglyph) panic("tmp_at: tglyph not initialized");
871
if (tglyph->style == DISP_BEAM) {
874
/* Erase (reset) from source to end */
875
for (i = 0; i < tglyph->sidx; i++)
876
newsym(tglyph->saved[i].x, tglyph->saved[i].y);
877
} else { /* DISP_FLASH or DISP_ALWAYS */
878
if (tglyph->sidx) /* been called at least once */
879
newsym(tglyph->saved[0].x, tglyph->saved[0].y);
881
/* tglyph->sidx = 0; -- about to be freed, so not necessary */
883
if (tglyph != &tgfirst) free((genericptr_t)tglyph);
888
if (tglyph->style == DISP_BEAM) {
889
if (!cansee(x,y)) break;
890
/* save pos for later erasing */
891
tglyph->saved[tglyph->sidx].x = x;
892
tglyph->saved[tglyph->sidx].y = y;
894
} else { /* DISP_FLASH/ALWAYS */
895
if (tglyph->sidx) { /* not first call, so reset previous pos */
896
newsym(tglyph->saved[0].x, tglyph->saved[0].y);
897
tglyph->sidx = 0; /* display is presently up to date */
899
if (!cansee(x,y) && tglyph->style != DISP_ALWAYS) break;
900
tglyph->saved[0].x = x;
901
tglyph->saved[0].y = y;
905
show_glyph(x, y, tglyph->glyph); /* show it */
906
flush_screen(0); /* make sure it shows up */
915
* The hero is swallowed. Show a special graphics sequence for this. This
916
* bypasses all of the display routines and messes with buffered screen
917
* directly. This method works because both vision and display check for
924
static xchar lastx, lasty; /* last swallowed position */
925
int swallower, left_ok, rght_ok;
932
/* Clear old location */
933
for (y = lasty-1; y <= lasty+1; y++)
934
for (x = lastx-1; x <= lastx+1; x++)
935
if (isok(x,y)) show_glyph(x,y,cmap_to_glyph(S_stone));
938
swallower = monsndx(u.ustuck->data);
939
/* assume isok(u.ux,u.uy) */
940
left_ok = isok(u.ux-1,u.uy);
941
rght_ok = isok(u.ux+1,u.uy);
943
* Display the hero surrounded by the monster's stomach.
945
if(isok(u.ux, u.uy-1)) {
947
show_glyph(u.ux-1, u.uy-1, swallow_to_glyph(swallower, S_sw_tl));
948
show_glyph(u.ux , u.uy-1, swallow_to_glyph(swallower, S_sw_tc));
950
show_glyph(u.ux+1, u.uy-1, swallow_to_glyph(swallower, S_sw_tr));
954
show_glyph(u.ux-1, u.uy , swallow_to_glyph(swallower, S_sw_ml));
957
show_glyph(u.ux+1, u.uy , swallow_to_glyph(swallower, S_sw_mr));
959
if(isok(u.ux, u.uy+1)) {
961
show_glyph(u.ux-1, u.uy+1, swallow_to_glyph(swallower, S_sw_bl));
962
show_glyph(u.ux , u.uy+1, swallow_to_glyph(swallower, S_sw_bc));
964
show_glyph(u.ux+1, u.uy+1, swallow_to_glyph(swallower, S_sw_br));
967
/* Update the swallowed position. */
975
* Similar to swallowed() in operation. Shows hero when underwater
976
* except when in water level. Special routines exist for that.
982
static xchar lastx, lasty;
986
/* swallowing has a higher precedence than under water */
987
if (Is_waterlevel(&u.uz) || u.uswallow) return;
990
if (mode == 1 || dela) {
994
/* delayed full update */
995
else if (mode == 2) {
1001
for (y = lasty-1; y <= lasty+1; y++)
1002
for (x = lastx-1; x <= lastx+1; x++)
1004
show_glyph(x,y,cmap_to_glyph(S_stone));
1006
for (x = u.ux-1; x <= u.ux+1; x++)
1007
for (y = u.uy-1; y <= u.uy+1; y++)
1008
if (isok(x,y) && is_pool(x,y)) {
1009
if (Blind && !(x == u.ux && y == u.uy))
1010
show_glyph(x,y,cmap_to_glyph(S_stone));
1021
* Very restricted display. You can only see yourself.
1027
static boolean dela;
1029
/* swallowing has a higher precedence than under ground */
1030
if (u.uswallow) return;
1033
if (mode == 1 || dela) {
1037
/* delayed full update */
1038
else if (mode == 2) {
1042
/* limited update */
1048
/* ========================================================================= */
1051
* Loop through all of the monsters and update them. Called when:
1052
* + going blind & telepathic
1053
* + regaining sight & telepathic
1054
* + getting and losing infravision
1056
* + doing a full screen redraw
1057
* + see invisible times out or a ring of see invisible is taken off
1058
* + when a potion of see invisible is quaffed or a ring of see
1059
* invisible is put on
1060
* + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1061
* + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1067
register struct monst *mon;
1069
for (mon = fmon; mon; mon = mon->nmon) {
1070
if (DEADMONSTER(mon)) continue;
1071
newsym(mon->mx,mon->my);
1072
if (mon->wormno) see_wsegs(mon);
1075
/* when mounted, hero's location gets caught by monster loop */
1082
* Block/unblock light depending on what a mimic is mimicing and if it's
1083
* invisible or not. Should be called only when the state of See_invisible
1087
set_mimic_blocking()
1089
register struct monst *mon;
1091
for (mon = fmon; mon; mon = mon->nmon) {
1092
if (DEADMONSTER(mon)) continue;
1094
((mon->m_ap_type == M_AP_FURNITURE &&
1095
(mon->mappearance == S_vcdoor || mon->mappearance == S_hcdoor)) ||
1096
(mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER))) {
1098
block_point(mon->mx, mon->my);
1100
unblock_point(mon->mx, mon->my);
1106
* Loop through all of the object *locations* and update them. Called when
1112
register struct obj *obj;
1113
for(obj = fobj; obj; obj = obj->nobj)
1114
if (vobj_at(obj->ox,obj->oy) == obj) newsym(obj->ox, obj->oy);
1118
* Update hallucinated traps.
1126
for (trap = ftrap; trap; trap = trap->ntrap) {
1127
glyph = glyph_at(trap->tx, trap->ty);
1128
if (glyph_is_trap(glyph))
1129
newsym(trap->tx, trap->ty);
1134
* Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1139
flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1153
register struct rm *lev;
1155
if (!u.ux) return; /* display isn't ready yet */
1161
if (Underwater && !Is_waterlevel(&u.uz)) {
1170
/* shut down vision */
1174
* This routine assumes that cls() does the following:
1175
* + fills the physical screen with the symbol for rock
1176
* + clears the glyph buffer
1180
/* display memory */
1181
for (x = 1; x < COLNO; x++) {
1183
for (y = 0; y < ROWNO; y++, lev++)
1184
if (lev->glyph != cmap_to_glyph(S_stone))
1185
show_glyph(x,y,lev->glyph);
1188
/* see what is to be seen */
1191
/* overlay with monsters */
1194
flags.botlx = 1; /* force a redraw of the bottom line */
1198
/* ========================================================================= */
1199
/* Glyph Buffering (3rd screen) ============================================ */
1202
xchar new; /* perhaps move this bit into the rm strucure. */
1206
static gbuf_entry gbuf[ROWNO][COLNO];
1207
static char gbuf_start[ROWNO];
1208
static char gbuf_stop[ROWNO];
1211
* Store the glyph in the 3rd screen for later flushing.
1214
show_glyph(x,y,glyph)
1218
* Check for bad positions and glyphs.
1224
/* column 0 is invalid, but it's often used as a flag, so ignore it */
1228
* This assumes an ordering of the offsets. See display.h for
1232
if (glyph >= GLYPH_WARNING_OFF) { /* a warning */
1233
text = "warning"; offset = glyph - GLYPH_WARNING_OFF;
1234
} else if (glyph >= GLYPH_SWALLOW_OFF) { /* swallow border */
1235
text = "swallow border"; offset = glyph - GLYPH_SWALLOW_OFF;
1236
} else if (glyph >= GLYPH_ZAP_OFF) { /* zap beam */
1237
text = "zap beam"; offset = glyph - GLYPH_ZAP_OFF;
1238
} else if (glyph >= GLYPH_EXPLODE_OFF) { /* explosion */
1239
text = "explosion"; offset = glyph - GLYPH_EXPLODE_OFF;
1240
} else if (glyph >= GLYPH_CMAP_OFF) { /* cmap */
1241
text = "cmap_index"; offset = glyph - GLYPH_CMAP_OFF;
1242
} else if (glyph >= GLYPH_OBJ_OFF) { /* object */
1243
text = "object"; offset = glyph - GLYPH_OBJ_OFF;
1244
} else if (glyph >= GLYPH_RIDDEN_OFF) { /* ridden mon */
1245
text = "ridden mon"; offset = glyph - GLYPH_RIDDEN_OFF;
1246
} else if (glyph >= GLYPH_BODY_OFF) { /* a corpse */
1247
text = "corpse"; offset = glyph - GLYPH_BODY_OFF;
1248
} else if (glyph >= GLYPH_DETECT_OFF) { /* detected mon */
1249
text = "detected mon"; offset = glyph - GLYPH_DETECT_OFF;
1250
} else if (glyph >= GLYPH_INVIS_OFF) { /* invisible mon */
1251
text = "invisible mon"; offset = glyph - GLYPH_INVIS_OFF;
1252
} else if (glyph >= GLYPH_PET_OFF) { /* a pet */
1253
text = "pet"; offset = glyph - GLYPH_PET_OFF;
1254
} else { /* a monster */
1255
text = "monster"; offset = glyph;
1258
impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].",
1259
x, y, glyph, text, offset);
1263
if (glyph >= MAX_GLYPH) {
1264
impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).",
1265
glyph, MAX_GLYPH, x, y);
1269
if (gbuf[y][x].glyph != glyph) {
1270
gbuf[y][x].glyph = glyph;
1272
if (gbuf_start[y] > x) gbuf_start[y] = x;
1273
if (gbuf_stop[y] < x) gbuf_stop[y] = x;
1279
* Reset the changed glyph borders so that none of the 3rd screen has
1282
#define reset_glyph_bbox() \
1286
for (i = 0; i < ROWNO; i++) { \
1287
gbuf_start[i] = COLNO-1; \
1293
static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1295
* Turn the 3rd screen into stone.
1298
clear_glyph_buffer()
1301
register gbuf_entry *gptr;
1303
for (y = 0; y < ROWNO; y++) {
1305
for (x = COLNO; x; x--) {
1313
* Assumes that the indicated positions are filled with S_stone glyphs.
1316
row_refresh(start,stop,y)
1321
for (x = start; x <= stop; x++)
1322
if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
1323
print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph);
1329
display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1330
flags.botlx = 1; /* force update of botl window */
1331
clear_nhwindow(WIN_MAP); /* clear physical screen */
1333
clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1337
* Synch the third screen with the display.
1340
flush_screen(cursor_on_u)
1343
/* Prevent infinite loops on errors:
1344
* flush_screen->print_glyph->impossible->pline->flush_screen
1346
static boolean flushing = 0;
1347
static boolean delay_flushing = 0;
1350
if (cursor_on_u == -1) delay_flushing = !delay_flushing;
1351
if (delay_flushing) return;
1352
if (flushing) return; /* if already flushing then return */
1355
for (y = 0; y < ROWNO; y++) {
1356
register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
1357
for (; x <= gbuf_stop[y]; gptr++, x++)
1359
print_glyph(WIN_MAP,x,y,gptr->glyph);
1364
if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */
1365
display_nhwindow(WIN_MAP, FALSE);
1368
if(flags.botl || flags.botlx) bot();
1371
/* ========================================================================= */
1376
* Use the information in the rm structure at the given position to create
1377
* a glyph of a background.
1379
* I had to add a field in the rm structure (horizontal) so that we knew
1380
* if open doors and secret doors were horizontal or vertical. Previously,
1381
* the screen symbol had the horizontal/vertical information set at
1382
* level generation time.
1384
* I used the 'ladder' field (really doormask) for deciding if stairwells
1385
* were up or down. I didn't want to check the upstairs and dnstairs
1393
struct rm *ptr = &(levl[x][y]);
1398
idx = level.flags.arboreal ? S_tree : S_stone;
1400
case ROOM: idx = S_room; break;
1402
idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1416
idx = ptr->seenv ? wall_angle(ptr) : S_stone;
1419
if (ptr->doormask) {
1420
if (ptr->doormask & D_BROKEN)
1422
else if (ptr->doormask & D_ISOPEN)
1423
idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
1424
else /* else is closed */
1425
idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
1429
case IRONBARS: idx = S_bars; break;
1430
case TREE: idx = S_tree; break;
1432
case MOAT: idx = S_pool; break;
1434
idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
1437
idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
1439
case FOUNTAIN: idx = S_fountain; break;
1440
case SINK: idx = S_sink; break;
1441
case ALTAR: idx = S_altar; break;
1442
case GRAVE: idx = S_grave; break;
1443
case THRONE: idx = S_throne; break;
1444
case LAVAPOOL: idx = S_lava; break;
1445
case ICE: idx = S_ice; break;
1446
case AIR: idx = S_air; break;
1447
case CLOUD: idx = S_cloud; break;
1448
case WATER: idx = S_water; break;
1450
idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
1453
switch(ptr->drawbridgemask & DB_UNDER) {
1454
case DB_MOAT: idx = S_pool; break;
1455
case DB_LAVA: idx = S_lava; break;
1456
case DB_ICE: idx = S_ice; break;
1457
case DB_FLOOR: idx = S_room; break;
1459
impossible("Strange db-under: %d",
1460
ptr->drawbridgemask & DB_UNDER);
1461
idx = S_room; /* something is better than nothing */
1465
case DRAWBRIDGE_DOWN:
1466
idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
1469
impossible("back_to_glyph: unknown level type [ = %d ]",ptr->typ);
1474
return cmap_to_glyph(idx);
1479
* swallow_to_glyph()
1481
* Convert a monster number and a swallow location into the correct glyph.
1482
* If you don't want a patchwork monster while hallucinating, decide on
1483
* a random monster in swallowed() and don't use what_mon() here.
1486
swallow_to_glyph(mnum, loc)
1490
if (loc < S_sw_tl || S_sw_br < loc) {
1491
impossible("swallow_to_glyph: bad swallow location");
1494
return ((int) (what_mon(mnum)<<3) | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
1502
* Change the given zap direction and beam type into a glyph. Each beam
1503
* type has four glyphs, one for each of the symbols below. The order of
1504
* the zap symbols [0-3] as defined in rm.h are:
1506
* | S_vbeam ( 0, 1) or ( 0,-1)
1507
* - S_hbeam ( 1, 0) or (-1, 0)
1508
* \ S_lslant ( 1, 1) or (-1,-1)
1509
* / S_rslant (-1, 1) or ( 1,-1)
1512
zapdir_to_glyph(dx, dy, beam_type)
1513
register int dx, dy;
1516
if (beam_type >= NUM_ZAP) {
1517
impossible("zapdir_to_glyph: illegal beam type");
1520
dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
1522
return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
1527
* Utility routine for dowhatis() used to find out the glyph displayed at
1528
* the location. This isn't necessarily the same as the glyph in the levl
1529
* structure, so we must check the "third screen".
1535
if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
1536
return cmap_to_glyph(S_room); /* XXX */
1537
return gbuf[y][x].glyph;
1541
/* ------------------------------------------------------------------------- */
1542
/* Wall Angle -------------------------------------------------------------- */
1544
/*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
1548
static const char *FDECL(type_to_name, (int));
1549
static void FDECL(error4, (int,int,int,int,int,int));
1551
static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1552
static const char *type_names[MAX_TYPE] = {
1553
"STONE", "VWALL", "HWALL", "TLCORNER",
1554
"TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL",
1555
"TUWALL", "TDWALL", "TLWALL", "TRWALL",
1556
"DBWALL", "SDOOR", "SCORR", "POOL",
1557
"MOAT", "WATER", "DRAWBRIDGE_UP","LAVAPOOL",
1558
"DOOR", "CORR", "ROOM", "STAIRS",
1559
"LADDER", "FOUNTAIN", "THRONE", "SINK",
1560
"ALTAR", "ICE", "DRAWBRIDGE_DOWN","AIR",
1569
return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type];
1573
error4(x, y, a, b, c, dd)
1574
int x, y, a, b, c, dd;
1576
pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1577
type_to_name(levl[x][y].typ), x, y,
1578
a ? "1":"", b ? "2":"", c ? "3":"", dd ? "4":"");
1579
bad_count[levl[x][y].typ]++;
1581
#endif /* WA_VERBOSE */
1584
* Return 'which' if position is implies an unfinshed exterior. Return
1585
* zero otherwise. Unfinished implies outer area is rock or a corridor.
1587
* Things that are ambigious: lava
1590
check_pos(x, y, which)
1594
if (!isok(x,y)) return which;
1595
type = levl[x][y].typ;
1596
if (IS_ROCK(type) || type == CORR || type == SCORR) return which;
1600
/* Return TRUE if more than one is non-zero. */
1604
more_than_one(x, y, a, b, c)
1607
if ((a && (b|c)) || (b && (a|c)) || (c && (a|b))) {
1608
error4(x,y,a,b,c,0);
1614
#define more_than_one(x, y, a, b, c) (((a) && ((b)|(c))) || ((b) && ((a)|(c))) || ((c) && ((a)|(b))))
1617
/* Return the wall mode for a T wall. */
1619
set_twall(x0,y0, x1,y1, x2,y2, x3,y3)
1620
int x0,y0, x1,y1, x2,y2, x3,y3;
1622
int wmode, is_1, is_2, is_3;
1624
is_1 = check_pos(x1, y1, WM_T_LONG);
1625
is_2 = check_pos(x2, y2, WM_T_BL);
1626
is_3 = check_pos(x3, y3, WM_T_BR);
1627
if (more_than_one(x0, y0, is_1, is_2, is_3)) {
1630
wmode = is_1 + is_2 + is_3;
1635
/* Return wall mode for a horizontal or vertical wall. */
1637
set_wall(x, y, horiz)
1640
int wmode, is_1, is_2;
1643
is_1 = check_pos(x,y-1, WM_W_TOP);
1644
is_2 = check_pos(x,y+1, WM_W_BOTTOM);
1646
is_1 = check_pos(x-1,y, WM_W_LEFT);
1647
is_2 = check_pos(x+1,y, WM_W_RIGHT);
1649
if (more_than_one(x, y, is_1, is_2, 0)) {
1652
wmode = is_1 + is_2;
1658
/* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
1660
set_corn(x1,y1, x2,y2, x3,y3, x4,y4)
1661
int x1, y1, x2, y2, x3, y3, x4, y4;
1663
int wmode, is_1, is_2, is_3, is_4;
1665
is_1 = check_pos(x1, y1, 1);
1666
is_2 = check_pos(x2, y2, 1);
1667
is_3 = check_pos(x3, y3, 1);
1668
is_4 = check_pos(x4, y4, 1); /* inner location */
1671
* All 4 should not be true. So if the inner location is rock,
1672
* use it. If all of the outer 3 are true, use outer. We currently
1673
* can't cover the case where only part of the outer is rock, so
1674
* we just say that all the walls are finished (if not overridden
1675
* by the inner section).
1679
} else if (is_1 && is_2 && is_3)
1682
wmode = 0; /* finished walls on all sides */
1687
/* Return mode for a crosswall. */
1692
int wmode, is_1, is_2, is_3, is_4;
1694
is_1 = check_pos(x-1, y-1, 1);
1695
is_2 = check_pos(x+1, y-1, 1);
1696
is_3 = check_pos(x+1, y+1, 1);
1697
is_4 = check_pos(x-1, y+1, 1);
1699
wmode = is_1+is_2+is_3+is_4;
1701
if (is_1 && is_3 && (is_2+is_4 == 0)) {
1703
} else if (is_2 && is_4 && (is_1+is_3 == 0)) {
1707
error4(x,y,is_1,is_2,is_3,is_4);
1723
/* Called from mklev. Scan the level and set the wall modes. */
1732
for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0;
1735
for (x = 0; x < COLNO; x++)
1736
for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
1739
wmode = set_wall(x, y, (int) lev->horizontal);
1742
wmode = set_wall(x, y, 0);
1745
wmode = set_wall(x, y, 1);
1748
wmode = set_twall(x,y, x,y-1, x-1,y+1, x+1,y+1);
1751
wmode = set_twall(x,y, x,y+1, x+1,y-1, x-1,y-1);
1754
wmode = set_twall(x,y, x+1,y, x-1,y-1, x-1,y+1);
1757
wmode = set_twall(x,y, x-1,y, x+1,y+1, x+1,y-1);
1760
wmode = set_corn(x-1,y-1, x,y-1, x-1,y, x+1,y+1);
1763
wmode = set_corn(x,y-1, x+1,y-1, x+1,y, x-1,y+1);
1766
wmode = set_corn(x,y+1, x-1,y+1, x-1,y, x+1,y-1);
1769
wmode = set_corn(x+1,y, x+1,y+1, x,y+1, x-1,y-1);
1772
wmode = set_crosswall(x, y);
1776
wmode = -1; /* don't set wall info */
1781
lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
1785
/* check if any bad positions found */
1786
for (x = y = 0; x < MAX_TYPE; x++)
1789
y = 1; /* only print once */
1790
pline("set_wall_type: wall mode problems with: ");
1792
pline("%s %d;", type_names[x], bad_count[x]);
1794
#endif /* WA_VERBOSE */
1797
/* ------------------------------------------------------------------------- */
1798
/* This matrix is used here and in vision.c. */
1799
unsigned char seenv_matrix[3][3] = { {SV2, SV1, SV0},
1803
#define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
1805
/* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
1807
set_seenv(lev, x0, y0, x, y)
1809
int x0, y0, x, y; /* from, to */
1811
int dx = x-x0, dy = y0-y;
1812
lev->seenv |= seenv_matrix[sign(dy)+1][sign(dx)+1];
1815
/* ------------------------------------------------------------------------- */
1817
/* T wall types, one for each row in wall_matrix[][]. */
1824
* These are the column names of wall_matrix[][]. They are the "results"
1825
* of a tdwall pattern match. All T walls are rotated so they become
1826
* a tdwall. Then we do a single pattern match, but return the
1827
* correct result for the original wall by using different rows for
1828
* each of the wall types.
1836
static const int wall_matrix[4][5] = {
1837
{ S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
1838
{ S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
1839
{ S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
1840
{ S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
1844
/* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][]. */
1851
* These are the column names for cross_matrix[][]. They express results
1852
* in C_br (bottom right) terms. All crosswalls with a single solid
1853
* quarter are rotated so the solid section is at the bottom right.
1854
* We pattern match on that, but return the correct result depending
1855
* on which row we'ere looking at.
1864
static const int cross_matrix[4][6] = {
1865
{ S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
1866
{ S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
1867
{ S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
1868
{ S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
1872
/* Print out a T wall warning and all interesting info. */
1877
static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
1880
if (lev->typ == TUWALL) wname = "tuwall";
1881
else if (lev->typ == TLWALL) wname = "tlwall";
1882
else if (lev->typ == TRWALL) wname = "trwall";
1883
else if (lev->typ == TDWALL) wname = "tdwall";
1884
else wname = "unknown";
1885
impossible(warn_str, wname, lev->wall_info & WM_MASK,
1886
(unsigned int) lev->seenv);
1891
* Return the correct graphics character index using wall type, wall mode,
1892
* and the seen vector. It is expected that seenv is non zero.
1894
* All T-wall vectors are rotated to be TDWALL. All single crosswall
1895
* blocks are rotated to bottom right. All double crosswall are rotated
1896
* to W_X_BLTR. All results are converted back.
1898
* The only way to understand this is to take out pen and paper and
1899
* draw diagrams. See rm.h for more details on the wall modes and
1906
register unsigned int seenv = lev->seenv & 0xff;
1910
#define only(sv, bits) (((sv) & (bits)) && ! ((sv) & ~(bits)))
1913
row = wall_matrix[T_u];
1914
seenv = (seenv >> 4 | seenv << 4) & 0xff;/* rotate to tdwall */
1917
row = wall_matrix[T_l];
1918
seenv = (seenv >> 2 | seenv << 6) & 0xff;/* rotate to tdwall */
1921
row = wall_matrix[T_r];
1922
seenv = (seenv >> 6 | seenv << 2) & 0xff;/* rotate to tdwall */
1925
row = wall_matrix[T_d];
1927
switch (lev->wall_info & WM_MASK) {
1931
} else if (seenv == SV6) {
1933
} else if (seenv & (SV3|SV5|SV7) ||
1934
((seenv & SV4) && (seenv & SV6))) {
1936
} else if (seenv & (SV0|SV1|SV2)) {
1937
col = (seenv & (SV4|SV6) ? T_tdwall : T_hwall);
1944
if (seenv & (SV3|SV4) && !(seenv & (SV5|SV6|SV7))) {
1946
} else if (seenv&(SV6|SV7) && !(seenv&(SV3|SV4|SV5))) {
1948
} else if ((seenv & SV5) ||
1949
((seenv & (SV3|SV4)) && (seenv & (SV6|SV7)))) {
1952
/* only SV0|SV1|SV2 */
1953
if (! only(seenv, SV0|SV1|SV2) )
1959
#if 0 /* older method, fixed */
1960
if (only(seenv, SV4|SV5)) {
1962
} else if ((seenv & (SV0|SV1|SV2)) &&
1963
only(seenv, SV0|SV1|SV2|SV6|SV7)) {
1965
} else if (seenv & SV3 ||
1966
((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) {
1974
if (only(seenv, SV4|SV5))
1976
else if ((seenv & (SV0|SV1|SV2|SV7)) &&
1977
!(seenv & (SV3|SV4|SV5)))
1979
else if (only(seenv, SV6))
1985
#if 0 /* older method, fixed */
1986
if (only(seenv, SV5|SV6)) {
1988
} else if ((seenv & (SV0|SV1|SV2)) &&
1989
only(seenv, SV0|SV1|SV2|SV3|SV4)) {
1991
} else if (seenv & SV7 ||
1992
((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) {
2000
if (only(seenv, SV5|SV6))
2002
else if ((seenv & (SV0|SV1|SV2|SV3)) &&
2003
!(seenv & (SV5|SV6|SV7)))
2005
else if (only(seenv, SV4))
2012
impossible("wall_angle: unknown T wall mode %d",
2013
lev->wall_info & WM_MASK);
2021
if (lev->horizontal) goto horiz;
2024
switch (lev->wall_info & WM_MASK) {
2025
case 0: idx = seenv ? S_vwall : S_stone; break;
2026
case 1: idx = seenv & (SV1|SV2|SV3|SV4|SV5) ? S_vwall :
2029
case 2: idx = seenv & (SV0|SV1|SV5|SV6|SV7) ? S_vwall :
2033
impossible("wall_angle: unknown vwall mode %d",
2034
lev->wall_info & WM_MASK);
2042
switch (lev->wall_info & WM_MASK) {
2043
case 0: idx = seenv ? S_hwall : S_stone; break;
2044
case 1: idx = seenv & (SV3|SV4|SV5|SV6|SV7) ? S_hwall :
2047
case 2: idx = seenv & (SV0|SV1|SV2|SV3|SV7) ? S_hwall :
2051
impossible("wall_angle: unknown hwall mode %d",
2052
lev->wall_info & WM_MASK);
2058
#define set_corner(idx, lev, which, outer, inner, name) \
2059
switch ((lev)->wall_info & WM_MASK) { \
2060
case 0: idx = which; break; \
2061
case WM_C_OUTER: idx = seenv & (outer) ? which : S_stone; break; \
2062
case WM_C_INNER: idx = seenv & ~(inner) ? which : S_stone; break; \
2064
impossible("wall_angle: unknown %s mode %d", name, \
2065
(lev)->wall_info & WM_MASK); \
2071
set_corner(idx, lev, S_tlcorn, (SV3|SV4|SV5), SV4, "tlcorn");
2074
set_corner(idx, lev, S_trcorn, (SV5|SV6|SV7), SV6, "trcorn");
2077
set_corner(idx, lev, S_blcorn, (SV1|SV2|SV3), SV2, "blcorn");
2080
set_corner(idx, lev, S_brcorn, (SV7|SV0|SV1), SV0, "brcorn");
2085
switch (lev->wall_info & WM_MASK) {
2089
else if (seenv == SV2)
2091
else if (seenv == SV4)
2093
else if (seenv == SV6)
2095
else if (!(seenv & ~(SV0|SV1|SV2)) &&
2096
(seenv & SV1 || seenv == (SV0|SV2)))
2098
else if (!(seenv & ~(SV2|SV3|SV4)) &&
2099
(seenv & SV3 || seenv == (SV2|SV4)))
2101
else if (!(seenv & ~(SV4|SV5|SV6)) &&
2102
(seenv & SV5 || seenv == (SV4|SV6)))
2104
else if (!(seenv & ~(SV0|SV6|SV7)) &&
2105
(seenv & SV7 || seenv == (SV0|SV6)))
2112
row = cross_matrix[C_tl];
2113
seenv = (seenv >> 4 | seenv << 4) & 0xff;
2116
row = cross_matrix[C_tr];
2117
seenv = (seenv >> 6 | seenv << 2) & 0xff;
2120
row = cross_matrix[C_bl];
2121
seenv = (seenv >> 2 | seenv << 6) & 0xff;
2124
row = cross_matrix[C_br];
2129
seenv = seenv & ~SV4; /* strip SV4 */
2132
} else if (seenv & (SV2|SV3)) {
2133
if (seenv & (SV5|SV6|SV7))
2135
else if (seenv & (SV0|SV1))
2139
} else if (seenv & (SV5|SV6)) {
2140
if (seenv & (SV1|SV2|SV3))
2142
else if (seenv & (SV0|SV7))
2146
} else if (seenv & SV1) {
2147
col = seenv & SV7 ? C_crwall : C_tuwall;
2148
} else if (seenv & SV7) {
2149
col = seenv & SV1 ? C_crwall : C_tlwall;
2152
"wall_angle: bottom of crwall check");
2161
if ( only(seenv, SV1|SV2|SV3) )
2163
else if ( only(seenv, SV5|SV6|SV7) )
2165
else if ( only(seenv, SV0|SV4) )
2172
if ( only(seenv, SV0|SV1|SV7) )
2174
else if ( only(seenv, SV3|SV4|SV5) )
2176
else if ( only(seenv, SV2|SV6) )
2183
impossible("wall_angle: unknown crosswall mode");
2190
impossible("wall_angle: unexpected wall type %d", lev->typ);