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. */
5
6 /*
7 * THE NEW DISPLAY CODE
8 *
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.
13 *
14 * The display system uses information from the vision system to decide
15 * what to draw at a given location. The routines for the vision system
16 * can be found in vision.c and vision.h. The routines for display can
17 * be found in this file (display.c) and display.h. The drawing routines
18 * are part of the window port. See doc/window.doc for the drawing
19 * interface.
20 *
21 * The display system deals with an abstraction called a glyph. Anything
22 * that could possibly be displayed has a unique glyph identifier.
23 *
24 * What is seen on the screen is a combination of what the hero remembers
25 * and what the hero currently sees. Objects and dungeon features (walls
26 * doors, etc) are remembered when out of sight. Monsters and temporary
27 * effects are not remembered. Each location on the level has an
28 * associated glyph. This is the hero's _memory_ of what he or she has
29 * seen there before.
30 *
31 * Display rules:
32 *
33 * If the location is in sight, display in order:
34 * visible (or sensed) monsters
35 * visible objects
36 * known traps
37 * background
38 *
39 * If the location is out of sight, display in order:
40 * sensed monsters (telepathy)
41 * memory
42 *
43 *
44 *
45 * Here is a list of the major routines in this file to be used externally:
46 *
47 * newsym
48 *
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.
53 *
54 *
55 * map_background
56 * map_object
57 * map_trap
58 * map_invisible
59 * unmap_object
60 *
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.
71 *
72 *
73 * show_glyph
74 *
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().
80 *
81 *
82 * see_monsters
83 * see_objects
84 * see_traps
85 *
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.
89 *
90 *
91 * tmp_at
92 *
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.
95 *
96 *
97 *
98 * Parts of the rm structure that are used:
99 *
100 * typ - What is really there.
101 * glyph - What the hero remembers. This will never be a monster.
102 * Monsters "float" above this.
103 * lit - True if the position is lit. An optimization for
104 * lit/unlit rooms.
105 * waslit - True if the position was *remembered* as lit.
106 * seenv - A vector of bits representing the directions from which the
107 * hero has seen this position. The vector's primary use is
108 * determining how walls are seen. E.g. a wall sometimes looks
109 * like stone on one side, but is seen as 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
112 * seen the location.
113 * flags - Additional information for the typ field. Different for
114 * each typ.
115 * horizontal - Indicates whether the wall or door is horizontal or
116 * vertical.
117 */
118 #include "hack.h"
119 #include "region.h"
120
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 *));
124
125 STATIC_DCL int FDECL(check_pos, (int, int, int));
126 #ifdef WA_VERBOSE
127 STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int));
128 #endif
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 *));
136
137 #ifdef INVISIBLE_OBJECTS
138 /*
139 * vobj_at()
140 *
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.
146 */
147 struct obj *
vobj_at(x,y)148 vobj_at(x,y)
149 xchar x,y;
150 {
151 register struct obj *obj = level.objects[x][y];
152
153 while (obj) {
154 if (!obj->oinvis || See_invisible) return obj;
155 obj = obj->nexthere;
156 }
157 return ((struct obj *) 0);
158 }
159 #endif /* else vobj_at() is defined in display.h */
160
161 /*
162 * magic_map_background()
163 *
164 * This function is similar to map_background (see below) except we pay
165 * attention to and correct unexplored, lit ROOM and CORR spots.
166 */
167 void
magic_map_background(x,y,show)168 magic_map_background(x, y, show)
169 xchar x,y;
170 int show;
171 {
172 int glyph = back_to_glyph(x,y); /* assumes hero can see x,y */
173 struct rm *lev = &levl[x][y];
174
175 /*
176 * Correct for out of sight lit corridors and rooms that the hero
177 * doesn't remember as lit.
178 */
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_darkroom);
183 else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr))
184 glyph = cmap_to_glyph(S_corr);
185 }
186 if (level.flags.hero_memory)
187 lev->glyph = glyph;
188 if (show) show_glyph(x,y, glyph);
189 lev->styp = lev->typ;
190 }
191
192 /*
193 * The routines map_background(), map_object(), and map_trap() could just
194 * as easily be:
195 *
196 * map_glyph(x,y,glyph,show)
197 *
198 * Which is called with the xx_to_glyph() in the call. Then I can get
199 * rid of 3 routines that don't do very much anyway. And then stop
200 * having to create fake objects and traps. However, I am reluctant to
201 * make this change.
202 */
203 /* FIXME: some of these use xchars for x and y, and some use ints. Make
204 * this consistent.
205 */
206
207 /*
208 * map_background()
209 *
210 * Make the real background part of our map. This routine assumes that
211 * the hero can physically see the location. Update the screen if directed.
212 */
213 void
map_background(x,y,show)214 map_background(x, y, show)
215 register xchar x,y;
216 register int show;
217 {
218 register int glyph = back_to_glyph(x,y);
219
220 if (level.flags.hero_memory)
221 levl[x][y].glyph = glyph;
222 if (show) show_glyph(x,y, glyph);
223 }
224
225 /*
226 * map_trap()
227 *
228 * Map the trap and print it out if directed. This routine assumes that the
229 * hero can physically see the location.
230 */
231 void
map_trap(trap,show)232 map_trap(trap, show)
233 register struct trap *trap;
234 register int show;
235 {
236 register int x = trap->tx, y = trap->ty;
237 register int glyph = trap_to_glyph(trap);
238
239 if (level.flags.hero_memory)
240 levl[x][y].glyph = glyph;
241 if (show) show_glyph(x, y, glyph);
242 }
243
244 /*
245 * map_object()
246 *
247 * Map the given object. This routine assumes that the hero can physically
248 * see the location of the object. Update the screen if directed.
249 */
250 void
map_object(obj,show)251 map_object(obj, show)
252 register struct obj *obj;
253 register int show;
254 {
255 register int x = obj->ox, y = obj->oy;
256 register int glyph = obj_to_glyph(obj);
257
258 if (level.flags.hero_memory)
259 levl[x][y].glyph = glyph;
260 if (show) show_glyph(x, y, glyph);
261 }
262
263 /*
264 * map_invisible()
265 *
266 * Make the hero remember that a square contains an invisible monster.
267 * This is a special case in that the square will continue to be displayed
268 * this way even when the hero is close enough to see it. To get rid of
269 * this and display the square's actual contents, use unmap_object() followed
270 * by newsym() if necessary.
271 */
272 void
map_invisible(x,y)273 map_invisible(x, y)
274 register xchar x, y;
275 {
276 if (x != u.ux || y != u.uy) { /* don't display I at hero's location */
277 if (level.flags.hero_memory)
278 levl[x][y].glyph = GLYPH_INVISIBLE;
279 show_glyph(x, y, GLYPH_INVISIBLE);
280 }
281 }
282
283 /*
284 * unmap_object()
285 *
286 * Remove something from the map when the hero realizes it's not there any
287 * more. Replace it with background or known trap, but not with any other
288 * If this is used for detection, a full screen update is imminent anyway;
289 * if this is used to get rid of an invisible monster notation, we might have
290 * to call newsym().
291 */
292 void
unmap_object(x,y)293 unmap_object(x, y)
294 register int x, y;
295 {
296 register struct trap *trap;
297
298 if (!level.flags.hero_memory) return;
299
300 if ((trap = t_at(x,y)) != 0 && trap->tseen && !covers_traps(x,y))
301 map_trap(trap, 0);
302 else if (levl[x][y].seenv) {
303 struct rm *lev = &levl[x][y];
304
305 map_background(x, y, 0);
306
307 /* turn remembered dark room squares dark */
308 if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) &&
309 lev->typ == ROOM)
310 lev->glyph = cmap_to_glyph(S_darkroom);
311 } else
312 levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
313 }
314
315
316 /*
317 * map_location()
318 *
319 * Make whatever at this location show up. This is only for non-living
320 * things. This will not handle feeling invisible objects correctly.
321 *
322 * Internal to display.c, this is a #define for speed.
323 */
324 #define _map_location(x,y,show) \
325 { \
326 register struct obj *obj; \
327 register struct trap *trap; \
328 \
329 if ((obj = vobj_at(x,y)) && !covers_objects(x,y)) \
330 map_object(obj,show); \
331 else if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y)) \
332 map_trap(trap,show); \
333 else \
334 map_background(x,y,show); \
335 }
336
337 void
map_location(x,y,show)338 map_location(x,y,show)
339 int x, y, show;
340 {
341 _map_location(x,y,show);
342 }
343
344 #define DETECTED 2
345 #define PHYSICALLY_SEEN 1
346 #define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my)))
347
348 /*
349 * display_monster()
350 *
351 * Note that this is *not* a map_XXXX() function! Monsters sort of float
352 * above everything.
353 *
354 * Yuck. Display body parts by recognizing that the display position is
355 * not the same as the monster position. Currently the only body part is
356 * a worm tail.
357 *
358 */
359 STATIC_OVL void
display_monster(x,y,mon,sightflags,worm_tail)360 display_monster(x, y, mon, sightflags, worm_tail)
361 register xchar x, y; /* display position */
362 register struct monst *mon; /* monster to display */
363 int sightflags; /* 1 if the monster is physically seen */
364 /* 2 if detected using Detect_monsters */
365 register xchar worm_tail; /* mon is actually a worm tail */
366 {
367 register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING);
368 register int sensed = mon_mimic &&
369 (Protection_from_shape_changers || sensemon(mon));
370 /*
371 * We must do the mimic check first. If the mimic is mimicing something,
372 * and the location is in sight, we have to change the hero's memory
373 * so that when the position is out of sight, the hero remembers what
374 * the mimic was mimicing.
375 */
376
377 if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) {
378 switch (mon->m_ap_type) {
379 default:
380 impossible("display_monster: bad m_ap_type value [ = %d ]",
381 (int) mon->m_ap_type);
382 case M_AP_NOTHING:
383 show_glyph(x, y, mon_to_glyph(mon));
384 break;
385
386 case M_AP_FURNITURE: {
387 /*
388 * This is a poor man's version of map_background(). I can't
389 * use map_background() because we are overriding what is in
390 * the 'typ' field. Maybe have map_background()'s parameters
391 * be (x,y,glyph) instead of just (x,y).
392 *
393 * mappearance is currently set to an S_ index value in
394 * makemon.c.
395 */
396 register int glyph = cmap_to_glyph(mon->mappearance);
397 levl[x][y].glyph = glyph;
398 if (!sensed) show_glyph(x,y, glyph);
399 break;
400 }
401
402 case M_AP_OBJECT: {
403 struct obj obj; /* Make a fake object to send */
404 /* to map_object(). */
405 obj.ox = x;
406 obj.oy = y;
407 obj.otyp = mon->mappearance;
408 obj.corpsenm = PM_TENGU; /* if mimicing a corpse */
409 map_object(&obj,!sensed);
410 break;
411 }
412
413 case M_AP_MONSTER:
414 show_glyph(x,y, monnum_to_glyph(what_mon((int)mon->mappearance)));
415 break;
416 }
417
418 }
419
420 /* If the mimic is unsucessfully mimicing something, display the monster */
421 if (!mon_mimic || sensed) {
422 int num;
423
424 /* [ALI] Only use detected glyphs when monster wouldn't be
425 * visible by any other means.
426 */
427 if (sightflags == DETECTED) {
428 if (worm_tail)
429 num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
430 else
431 num = detected_mon_to_glyph(mon);
432 } else if (mon->mtame && !Hallucination) {
433 if (worm_tail)
434 num = petnum_to_glyph(PM_LONG_WORM_TAIL);
435 else
436 num = pet_to_glyph(mon);
437 } else {
438 if (worm_tail)
439 num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
440 else
441 num = mon_to_glyph(mon);
442 }
443 show_glyph(x,y,num);
444 }
445 }
446
447 /*
448 * display_warning()
449 *
450 * This is also *not* a map_XXXX() function! Monster warnings float
451 * above everything just like monsters do, but only if the monster
452 * is not showing.
453 *
454 * Do not call for worm tails.
455 */
456 STATIC_OVL void
display_warning(mon)457 display_warning(mon)
458 register struct monst *mon;
459 {
460 int x = mon->mx, y = mon->my;
461 int wl = (int) (mon->m_lev / 4);
462 int glyph;
463
464 if (mon_warning(mon)) {
465 if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1;
466 /* 3.4.1: this really ought to be rn2(WARNCOUNT), but value "0"
467 isn't handled correctly by the what_is routine so avoid it */
468 if (Hallucination) wl = rn1(WARNCOUNT-1,1);
469 glyph = warning_to_glyph(wl);
470 } else if (MATCH_WARN_OF_MON(mon)) {
471 glyph = mon_to_glyph(mon);
472 } else {
473 warning("display_warning did not match warning type?");
474 return;
475 }
476 show_glyph(x, y, glyph);
477 }
478
479 /*
480 * feel_location()
481 *
482 * Feel the given location. This assumes that the hero is blind and that
483 * the given position is either the hero's or one of the eight squares
484 * adjacent to the hero (except for a boulder push).
485 * If an invisible monster has gone away, that will be discovered. If an
486 * invisible monster has appeared, this will _not_ be discovered since
487 * searching only finds one monster per turn so we must check that separately.
488 */
489 void
feel_location(x,y)490 feel_location(x, y)
491 xchar x, y;
492 {
493 struct rm *lev = &(levl[x][y]);
494 struct obj *boulder;
495 register struct monst *mon;
496
497 /* If the hero's memory of an invisible monster is accurate, we want to keep
498 * him from detecting the same monster over and over again on each turn.
499 * We must return (so we don't erase the monster). (We must also, in the
500 * search function, be sure to skip over previously detected 'I's.)
501 */
502 if (glyph_is_invisible(levl[x][y].glyph) && m_at(x,y)) return;
503
504 /* The hero can't feel non pool locations while under water. */
505 if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(x,y))
506 return;
507
508 /* Set the seen vector as if the hero had seen it. It doesn't matter */
509 /* if the hero is levitating or not. */
510 set_seenv(lev, u.ux, u.uy, x, y);
511
512 if (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) {
513 /*
514 * Levitation Rules. It is assumed that the hero can feel the state
515 * of the walls around herself and can tell if she is in a corridor,
516 * room, or doorway. Boulders are felt because they are large enough.
517 * Anything else is unknown because the hero can't reach the ground.
518 * This makes things difficult.
519 *
520 * Check (and display) in order:
521 *
522 * + Stone, walls, and closed doors.
523 * + Boulders. [see a boulder before a doorway]
524 * + Doors.
525 * + Room/water positions
526 * + Everything else (hallways!)
527 */
528 if (IS_ROCK(lev->typ) || (IS_DOOR(lev->typ) &&
529 (lev->doormask & (D_LOCKED | D_CLOSED)))) {
530 map_background(x, y, 1);
531 } else if ((boulder = sobj_at(BOULDER,x,y)) != 0) {
532 map_object(boulder, 1);
533 } else if (IS_DOOR(lev->typ)) {
534 map_background(x, y, 1);
535 } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
536 /*
537 * An open room or water location. Normally we wouldn't touch
538 * this, but we have to get rid of remembered boulder symbols.
539 * This will only occur in rare occations when the hero goes
540 * blind and doesn't find a boulder where expected (something
541 * came along and picked it up). We know that there is not a
542 * boulder at this location. Show fountains, pools, etc.
543 * underneath if already seen. Otherwise, show the appropriate
544 * floor symbol.
545 *
546 * Similarly, if the hero digs a hole in a wall or feels a location
547 * that used to contain an unseen monster. In these cases,
548 * there's no reason to assume anything was underneath, so
549 * just show the appropriate floor symbol. If something was
550 * embedded in the wall, the glyph will probably already
551 * reflect that. Don't change the symbol in this case.
552 *
553 * This isn't quite correct. If the boulder was on top of some
554 * other objects they should be seen once the boulder is removed.
555 * However, we have no way of knowing that what is there now
556 * was there then. So we let the hero have a lapse of memory.
557 * We could also just display what is currently on the top of the
558 * object stack (if anything).
559 */
560 if (lev->glyph == objnum_to_glyph(BOULDER)) {
561 if (lev->typ != ROOM && lev->seenv) {
562 map_background(x, y, 1);
563 } else {
564 lev->glyph = (!lev->waslit) ? cmap_to_glyph(S_darkroom) : cmap_to_glyph(S_room);
565 show_glyph(x,y,lev->glyph);
566 }
567 } else if ((lev->glyph >= cmap_to_glyph(S_stone) &&
568 lev->glyph < cmap_to_glyph(S_darkroom)) ||
569 glyph_is_invisible(levl[x][y].glyph)) {
570 lev->glyph = (!cansee(x,y) && !lev->waslit) ? cmap_to_glyph(S_darkroom) :
571 cmap_to_glyph(S_room);
572 show_glyph(x,y,lev->glyph);
573 }
574 } else {
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));
582 }
583 } else {
584 _map_location(x, y, 1);
585
586 if (Punished) {
587 /*
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
592 * the wrong glyph.
593 */
594 if (uchain->ox == x && uchain->oy == y) {
595 if (level.objects[x][y] == uchain)
596 u.bc_felt |= BC_CHAIN;
597 else
598 u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
599 }
600 if (!carried(uball) && uball->ox == x && uball->oy == y) {
601 if (level.objects[x][y] == uball)
602 u.bc_felt |= BC_BALL;
603 else
604 u.bc_felt &= ~BC_BALL; /* do not feel the ball */
605 }
606 }
607
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))
611 show_glyph(x,y, lev->glyph = cmap_to_glyph(S_darkroom));
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));
615 }
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,
620 is_worm_tail(mon));
621 }
622
623 /*
624 * newsym()
625 *
626 * Possibly put a new glyph at the given location.
627 */
628 void
newsym(x,y)629 newsym(x,y)
630 register int x,y;
631 {
632 register struct monst *mon;
633 register struct rm *lev = &(levl[x][y]);
634 register int see_it;
635 register xchar worm_tail;
636
637 if (in_mklev) return;
638
639 /* only permit updating the hero when swallowed */
640 if (u.uswallow) {
641 if (x == u.ux && y == u.uy) display_self();
642 return;
643 }
644 if (Underwater && !Is_waterlevel(&u.uz)) {
645 /* don't do anything unless (x,y) is an adjacent underwater position */
646 int dx, dy;
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;
651 }
652
653 /* Can physically see the location. */
654 if (cansee(x,y)) {
655 NhRegion* reg = visible_region_at(x,y);
656 /*
657 * Don't use templit here: E.g.
658 *
659 * lev->waslit = !!(lev->lit || templit(x,y));
660 *
661 * Otherwise we have the "light pool" problem, where non-permanently
662 * lit areas just out of sight stay remembered as lit. They should
663 * re-darken.
664 *
665 * Perhaps ALL areas should revert to their "unlit" look when
666 * out of sight.
667 */
668 lev->waslit = (lev->lit!=0); /* remember lit condition */
669
670 if (reg != NULL && ACCESSIBLE(lev->typ)) {
671 show_region(reg,x,y);
672 return;
673 }
674 if (x == u.ux && y == u.uy) {
675 if (senseself()) {
676 _map_location(x,y,0); /* map *under* self */
677 display_self();
678 } else
679 /* we can see what is there */
680 _map_location(x,y,1);
681 }
682 else {
683 mon = m_at(x,y);
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))) {
689 if (mon->mtrapped) {
690 struct trap *trap = t_at(x, y);
691 int tt = trap ? trap->ttyp : NO_TRAP;
692
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) {
696 trap->tseen = TRUE;
697 }
698 }
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);
702 }
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))
706 map_invisible(x, y);
707 else
708 _map_location(x,y,1); /* map the location */
709 }
710 }
711
712 /* Can't see the location. */
713 else {
714 if (x == u.ux && y == u.uy) {
715 feel_location(u.ux, u.uy); /* forces an update */
716
717 if (senseself()) display_self();
718 }
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))))
722 || Detect_monsters)
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);
727 }
728 else if ((mon = m_at(x,y)) && mon_warning(mon) &&
729 !is_worm_tail(mon)) {
730 display_warning(mon);
731 }
732
733 /*
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:
736 *
737 * (1) A dark location that the hero could see through night
738 * vision.
739 *
740 * (2) Darkened while out of the hero's sight. This can happen
741 * when cursed scroll of light is read.
742 *
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.
745 *
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.
751 *
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.
754 */
755 else if (!lev->waslit || (iflags.dark_room && iflags.use_color)) {
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_darkroom));
760 else
761 goto show_mem;
762 } else {
763 show_mem:
764 show_glyph(x, y, lev->glyph);
765 }
766 }
767 }
768
769 #undef is_worm_tail
770
771 /*
772 * shieldeff()
773 *
774 * Put magic shield pyrotechnics at the given location. This *could* be
775 * pulled into a platform dependent routine for fancier graphics if desired.
776 */
777 void
shieldeff(x,y)778 shieldeff(x,y)
779 xchar x,y;
780 {
781 register int i;
782
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 */
788 delay_output();
789 }
790 newsym(x,y); /* restore the old information */
791 }
792 }
793
794
795 /*
796 * tmp_at()
797 *
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].
801 *
802 * Call:
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
808 * matter)
809 * (DISP_FREEMEM, 0) only used to prevent memory leak during
810 * exit)
811 * (x, y) display the glyph at the location
812 *
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.
818 */
819
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;
826 } tgfirst;
827
828 void
tmp_at(x,y)829 tmp_at(x, y)
830 int x, y;
831 {
832 static struct tmp_glyph *tglyph = (struct tmp_glyph *)0;
833 struct tmp_glyph *tmp;
834
835 switch (x) {
836 case DISP_BEAM:
837 case DISP_FLASH:
838 case DISP_ALWAYS:
839 if (!tglyph)
840 tmp = &tgfirst;
841 else /* nested effect; we need dynamic memory */
842 tmp = (struct tmp_glyph *)alloc(sizeof (struct tmp_glyph));
843 tmp->prev = tglyph;
844 tglyph = tmp;
845 tglyph->sidx = 0;
846 tglyph->style = x;
847 tglyph->glyph = y;
848 flush_screen(0); /* flush buffered glyphs */
849 return;
850
851 case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
852 while (tglyph) {
853 tmp = tglyph->prev;
854 if (tglyph != &tgfirst) free((genericptr_t)tglyph);
855 tglyph = tmp;
856 }
857 return;
858
859 default:
860 break;
861 }
862
863 if (!tglyph) panic("tmp_at: tglyph not initialized");
864
865 switch (x) {
866 case DISP_CHANGE:
867 tglyph->glyph = y;
868 break;
869
870 case DISP_END:
871 if (tglyph->style == DISP_BEAM) {
872 register int i;
873
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);
880 }
881 /* tglyph->sidx = 0; -- about to be freed, so not necessary */
882 tmp = tglyph->prev;
883 if (tglyph != &tgfirst) free((genericptr_t)tglyph);
884 tglyph = tmp;
885 break;
886
887 default: /* do it */
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;
893 tglyph->sidx += 1;
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 */
898 }
899 if (!cansee(x,y) && tglyph->style != DISP_ALWAYS) break;
900 tglyph->saved[0].x = x;
901 tglyph->saved[0].y = y;
902 tglyph->sidx = 1;
903 }
904
905 show_glyph(x, y, tglyph->glyph); /* show it */
906 flush_screen(0); /* make sure it shows up */
907 break;
908 } /* end case */
909 }
910
911
912 /*
913 * swallowed()
914 *
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
918 * being swallowed.
919 */
920 void
swallowed(first)921 swallowed(first)
922 int first;
923 {
924 static xchar lastx, lasty; /* last swallowed position */
925 int swallower, left_ok, rght_ok;
926
927 if (first)
928 cls();
929 else {
930 register int x, y;
931
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));
936 }
937
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);
942 /*
943 * Display the hero surrounded by the monster's stomach.
944 */
945 if(isok(u.ux, u.uy-1)) {
946 if (left_ok)
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));
949 if (rght_ok)
950 show_glyph(u.ux+1, u.uy-1, swallow_to_glyph(swallower, S_sw_tr));
951 }
952
953 if (left_ok)
954 show_glyph(u.ux-1, u.uy , swallow_to_glyph(swallower, S_sw_ml));
955 display_self();
956 if (rght_ok)
957 show_glyph(u.ux+1, u.uy , swallow_to_glyph(swallower, S_sw_mr));
958
959 if(isok(u.ux, u.uy+1)) {
960 if (left_ok)
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));
963 if (rght_ok)
964 show_glyph(u.ux+1, u.uy+1, swallow_to_glyph(swallower, S_sw_br));
965 }
966
967 /* Update the swallowed position. */
968 lastx = u.ux;
969 lasty = u.uy;
970 }
971
972 /*
973 * under_water()
974 *
975 * Similar to swallowed() in operation. Shows hero when underwater
976 * except when in water level. Special routines exist for that.
977 */
978 void
under_water(mode)979 under_water(mode)
980 int mode;
981 {
982 static xchar lastx, lasty;
983 static boolean dela;
984 register int x, y;
985
986 /* swallowing has a higher precedence than under water */
987 if (Is_waterlevel(&u.uz) || u.uswallow) return;
988
989 /* full update */
990 if (mode == 1 || dela) {
991 cls();
992 dela = FALSE;
993 }
994 /* delayed full update */
995 else if (mode == 2) {
996 dela = TRUE;
997 return;
998 }
999 /* limited update */
1000 else {
1001 for (y = lasty-1; y <= lasty+1; y++)
1002 for (x = lastx-1; x <= lastx+1; x++)
1003 if (isok(x,y))
1004 show_glyph(x,y,cmap_to_glyph(S_stone));
1005 }
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));
1011 else
1012 newsym(x,y);
1013 }
1014 lastx = u.ux;
1015 lasty = u.uy;
1016 }
1017
1018 /*
1019 * under_ground()
1020 *
1021 * Very restricted display. You can only see yourself.
1022 */
1023 void
under_ground(mode)1024 under_ground(mode)
1025 int mode;
1026 {
1027 static boolean dela;
1028
1029 /* swallowing has a higher precedence than under ground */
1030 if (u.uswallow) return;
1031
1032 /* full update */
1033 if (mode == 1 || dela) {
1034 cls();
1035 dela = FALSE;
1036 }
1037 /* delayed full update */
1038 else if (mode == 2) {
1039 dela = TRUE;
1040 return;
1041 }
1042 /* limited update */
1043 else
1044 newsym(u.ux,u.uy);
1045 }
1046
1047
1048 /* ========================================================================= */
1049
1050 /*
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
1055 * + hallucinating
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
1062 * sit.c]
1063 */
1064 void
see_monsters()1065 see_monsters()
1066 {
1067 register struct monst *mon;
1068
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);
1073 }
1074 #ifdef STEED
1075 /* when mounted, hero's location gets caught by monster loop */
1076 if (!u.usteed)
1077 #endif
1078 newsym(u.ux, u.uy);
1079 }
1080
1081 /*
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
1084 * changes.
1085 */
1086 void
set_mimic_blocking()1087 set_mimic_blocking()
1088 {
1089 register struct monst *mon;
1090
1091 for (mon = fmon; mon; mon = mon->nmon) {
1092 if (DEADMONSTER(mon)) continue;
1093 if (mon->minvis &&
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))) {
1097 if(See_invisible)
1098 block_point(mon->mx, mon->my);
1099 else
1100 unblock_point(mon->mx, mon->my);
1101 }
1102 }
1103 }
1104
1105 /*
1106 * Loop through all of the object *locations* and update them. Called when
1107 * + hallucinating.
1108 */
1109 void
see_objects()1110 see_objects()
1111 {
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);
1115 }
1116
1117 /*
1118 * Update hallucinated traps.
1119 */
1120 void
see_traps()1121 see_traps()
1122 {
1123 struct trap *trap;
1124 int glyph;
1125
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);
1130 }
1131 }
1132
1133 /*
1134 * Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1135 */
1136 void
curs_on_u()1137 curs_on_u()
1138 {
1139 flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1140 }
1141
1142 int
doredraw()1143 doredraw()
1144 {
1145 docrt();
1146 return 0;
1147 }
1148
1149 void
docrt()1150 docrt()
1151 {
1152 register int x,y;
1153 register struct rm *lev;
1154
1155 if (!u.ux) return; /* display isn't ready yet */
1156
1157 if (u.uswallow) {
1158 swallowed(1);
1159 return;
1160 }
1161 if (Underwater && !Is_waterlevel(&u.uz)) {
1162 under_water(1);
1163 return;
1164 }
1165 if (u.uburied) {
1166 under_ground(1);
1167 return;
1168 }
1169
1170 /* shut down vision */
1171 vision_recalc(2);
1172
1173 /*
1174 * This routine assumes that cls() does the following:
1175 * + fills the physical screen with the symbol for rock
1176 * + clears the glyph buffer
1177 */
1178 cls();
1179
1180 /* display memory */
1181 for (x = 1; x < COLNO; x++) {
1182 lev = &levl[x][0];
1183 for (y = 0; y < ROWNO; y++, lev++)
1184 if (lev->glyph != cmap_to_glyph(S_stone))
1185 show_glyph(x,y,lev->glyph);
1186 }
1187
1188 /* see what is to be seen */
1189 vision_recalc(0);
1190
1191 /* overlay with monsters */
1192 see_monsters();
1193
1194 flags.botlx = 1; /* force a redraw of the bottom line */
1195 }
1196
1197
1198 /* ========================================================================= */
1199 /* Glyph Buffering (3rd screen) ============================================ */
1200
1201 typedef struct {
1202 xchar new; /* perhaps move this bit into the rm strucure. */
1203 int glyph;
1204 } gbuf_entry;
1205
1206 static gbuf_entry gbuf[ROWNO][COLNO];
1207 static char gbuf_start[ROWNO];
1208 static char gbuf_stop[ROWNO];
1209
1210 /*
1211 * Store the glyph in the 3rd screen for later flushing.
1212 */
1213 void
show_glyph(x,y,glyph)1214 show_glyph(x,y,glyph)
1215 int x, y, glyph;
1216 {
1217 /*
1218 * Check for bad positions and glyphs.
1219 */
1220 if (!isok(x, y)) {
1221 const char *text;
1222 int offset;
1223
1224 /* column 0 is invalid, but it's often used as a flag, so ignore it */
1225 if (x == 0) return;
1226
1227 /*
1228 * This assumes an ordering of the offsets. See display.h for
1229 * the definition.
1230 */
1231
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;
1256 }
1257
1258 warning("show_glyph: bad pos %d %d with glyph %d [%s %d].",
1259 x, y, glyph, text, offset);
1260 return;
1261 }
1262
1263 if (glyph >= MAX_GLYPH) {
1264 warning("show_glyph: bad glyph %d [max %d] at (%d,%d).",
1265 glyph, MAX_GLYPH, x, y);
1266 return;
1267 }
1268
1269 #ifdef VULTURE_GRAPHICS
1270 /* for vulture we must add any glyph that is passed to show_glyph to the glyph buffer
1271 * otherwise jtp_print_glyph does not "see" that location.
1272 * For example when a monster picks up an item the topmost glyph will be the monster,
1273 * which does not change. However we need to see that the item is no longer there.*/
1274 {
1275 #else
1276 if (gbuf[y][x].glyph != glyph) {
1277 #endif
1278 gbuf[y][x].glyph = glyph;
1279 gbuf[y][x].new = 1;
1280 if (gbuf_start[y] > x) gbuf_start[y] = x;
1281 if (gbuf_stop[y] < x) gbuf_stop[y] = x;
1282 }
1283 }
1284
1285
1286 /*
1287 * Reset the changed glyph borders so that none of the 3rd screen has
1288 * changed.
1289 */
1290 #define reset_glyph_bbox() \
1291 { \
1292 int i; \
1293 \
1294 for (i = 0; i < ROWNO; i++) { \
1295 gbuf_start[i] = COLNO-1; \
1296 gbuf_stop[i] = 0; \
1297 } \
1298 }
1299
1300
1301 static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1302 /*
1303 * Turn the 3rd screen into stone.
1304 */
1305 void
clear_glyph_buffer()1306 clear_glyph_buffer()
1307 {
1308 register int x, y;
1309 register gbuf_entry *gptr;
1310
1311 for (y = 0; y < ROWNO; y++) {
1312 gptr = &gbuf[y][0];
1313 for (x = COLNO; x; x--) {
1314 *gptr++ = nul_gbuf;
1315 }
1316 }
1317 reset_glyph_bbox();
1318 }
1319
1320 /*
1321 * Assumes that the indicated positions are filled with S_stone glyphs.
1322 */
1323 void
row_refresh(start,stop,y)1324 row_refresh(start,stop,y)
1325 int start,stop,y;
1326 {
1327 register int x;
1328
1329 for (x = start; x <= stop; x++)
1330 if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
1331 print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph);
1332 }
1333
1334 void
cls()1335 cls()
1336 {
1337 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1338 flags.botlx = 1; /* force update of botl window */
1339 clear_nhwindow(WIN_MAP); /* clear physical screen */
1340
1341 clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1342 }
1343
1344 /*
1345 * Synch the third screen with the display.
1346 */
1347 void
flush_screen(cursor_on_u)1348 flush_screen(cursor_on_u)
1349 int cursor_on_u;
1350 {
1351 /* Prevent infinite loops on errors:
1352 * flush_screen->print_glyph->impossible->pline->flush_screen
1353 */
1354 static boolean flushing = 0;
1355 static boolean delay_flushing = 0;
1356 register int x,y;
1357
1358 if (cursor_on_u == -1) delay_flushing = !delay_flushing;
1359 if (delay_flushing) return;
1360 if (flushing) return; /* if already flushing then return */
1361 flushing = 1;
1362
1363 for (y = 0; y < ROWNO; y++) {
1364 register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
1365 for (; x <= gbuf_stop[y]; gptr++, x++)
1366 if (gptr->new) {
1367 print_glyph(WIN_MAP,x,y,gptr->glyph);
1368 gptr->new = 0;
1369 }
1370 }
1371
1372 if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */
1373 display_nhwindow(WIN_MAP, FALSE);
1374 reset_glyph_bbox();
1375 flushing = 0;
1376 if(flags.botl || flags.botlx) bot();
1377 }
1378
1379 /* ========================================================================= */
1380
1381 #ifdef DUMP_LOG
1382 /* D: Added to dump screen to output file */
get_glyph_char(glyph,oclass)1383 STATIC_PTR uchar get_glyph_char(glyph, oclass)
1384 int glyph;
1385 int *oclass;
1386 {
1387 uchar ch;
1388 register int offset;
1389 *oclass = 0;
1390
1391 if (glyph >= NO_GLYPH)
1392 return ' ';
1393
1394 /*
1395 * Map the glyph back to a character.
1396 *
1397 * Warning: For speed, this makes an assumption on the order of
1398 * offsets. The order is set in display.h.
1399 */
1400 if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */
1401 ch = def_warnsyms[offset].sym;
1402 } else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */
1403 /* see swallow_to_glyph() in display.c */
1404 ch = (uchar) defsyms[S_sw_tl + (offset & 0x7)].sym;
1405 } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */
1406 /* see zapdir_to_glyph() in display.c */
1407 ch = defsyms[S_vbeam + (offset & 0x3)].sym;
1408 } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) { /* cmap */
1409 ch = defsyms[offset].sym;
1410 *oclass = -1;
1411 } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) { /* object */
1412 ch = def_oc_syms[(int)objects[offset].oc_class];
1413 *oclass = (int)objects[offset].oc_class;
1414 } else if ((offset = (glyph - GLYPH_RIDDEN_OFF)) >= 0) { /* mon ridden */
1415 ch = def_monsyms[(int)mons[offset].mlet];
1416 } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) { /* a corpse */
1417 ch = def_oc_syms[(int)objects[CORPSE].oc_class];
1418 } else if ((offset = (glyph - GLYPH_DETECT_OFF)) >= 0) { /* mon detect */
1419 ch = def_monsyms[(int)mons[offset].mlet];
1420 } else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) { /* invisible */
1421 ch = DEF_INVISIBLE;
1422 } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) { /* a pet */
1423 ch = def_monsyms[(int)mons[offset].mlet];
1424 } else { /* a monster */
1425 ch = monsyms[(int)mons[glyph].mlet];
1426 }
1427 return ch;
1428 }
1429
1430 #ifdef TTY_GRAPHICS
1431 extern const char * FDECL(compress_str, (const char *));
1432 #else
1433 const char*
compress_str(str)1434 compress_str(str) /* copied from win/tty/wintty.c */
1435 const char *str;
1436 {
1437 static char cbuf[BUFSZ];
1438 /* compress in case line too long */
1439 if((int)strlen(str) >= 80) {
1440 register const char *bp0 = str;
1441 register char *bp1 = cbuf;
1442
1443 do {
1444 if(*bp0 != ' ' || bp0[1] != ' ')
1445 *bp1++ = *bp0;
1446 } while(*bp0++);
1447 } else
1448 return str;
1449 return cbuf;
1450 }
1451 #endif /* TTY_GRAPHICS */
1452
1453 /* Take a screen dump */
dump_screen()1454 void dump_screen()
1455 {
1456 register int x,y;
1457 int lastc = -1;
1458 /* D: botl.c has a closer approximation to the size, but we'll go with
1459 * this */
1460 char buf[COLNO*100], html_buf[COLNO*100], tmpbuf[100], *ptr;
1461 int ch, glyph, oclass;
1462 int color;
1463 unsigned special;
1464
1465 dump_html("<pre class=\"nh_screen\">\n", "");
1466 for (y = 0; y < ROWNO; y++) {
1467 buf[0] = '\0';
1468 html_buf[0] = '\0';
1469 lastc = 0;
1470 for (x = 1; x < COLNO; x++) {
1471 /* map glyph to character and color */
1472 glyph = gbuf[y][x].glyph;
1473 mapglyph(glyph, &ch, &color, &special, x, y);
1474 /* we can't use ch for output as that may be non-ASCII due
1475 * to DEC- or IBMgraphics */
1476 uchar c = get_glyph_char(glyph, &oclass);
1477 if (c == ' ')
1478 Strcpy(tmpbuf, " ");
1479 else if (x == u.ux && y == u.uy)
1480 Sprintf(tmpbuf, "<span class=\"nh_inv_%d nh_player\">%c</span>", color, c);
1481 else if (special & (MG_PET|MG_DETECT))
1482 Sprintf(tmpbuf, "<span class=\"nh_inv_%d nh_pet\">%c</span>", color, c);
1483 else if (special & (MG_PET|MG_DETECT))
1484 Sprintf(tmpbuf, "<span class=\"nh_inv_%d\">%c</span>", color, c);
1485 else if (special & MG_INVERSE)
1486 Sprintf(tmpbuf, "<span class=\"nh_inv_%d\">%c</span>", color, c);
1487 else if (oclass < 0 && IS_DOOR(levl[x][y].typ) && levl[x][y].doormask >= D_ISOPEN)
1488 Sprintf(tmpbuf, "<span class=\"nh_door\">%c</span>", c);
1489 else if (oclass < 0 && IS_DRAWBRIDGE(levl[x][y].typ))
1490 Sprintf(tmpbuf, "<span class=\"nh_drawbridge\">%c</span>", c);
1491 else if (oclass < 0 && levl[x][y].typ == POOL)
1492 Sprintf(tmpbuf, "<span class=\"nh_pool\">%c</span>", c);
1493 else if (oclass < 0 && levl[x][y].typ == MOAT)
1494 Sprintf(tmpbuf, "<span class=\"nh_moat\">%c</span>", c);
1495 else if (oclass < 0 && levl[x][y].typ == WATER)
1496 Sprintf(tmpbuf, "<span class=\"nh_water\">%c</span>", c);
1497 else if (oclass < 0 && levl[x][y].typ == LAVAPOOL)
1498 Sprintf(tmpbuf, "<span class=\"nh_lava\">%c</span>", c);
1499 else if (oclass < 0 && levl[x][y].typ == IRONBARS)
1500 Sprintf(tmpbuf, "<span class=\"nh_ironbars\">%c</span>", c);
1501 else if (oclass < 0 && levl[x][y].typ == CORR)
1502 Sprintf(tmpbuf, "<span class=\"nh_corridor\">%c</span>", c);
1503 else if (oclass < 0 && levl[x][y].typ == STAIRS)
1504 Sprintf(tmpbuf, "<span class=\"nh_stairs\">%s</span>", html_escape_character(c));
1505 else if (oclass < 0 && levl[x][y].typ == LADDER)
1506 Sprintf(tmpbuf, "<span class=\"nh_ladder\">%c</span>", c);
1507 else if (oclass < 0 && levl[x][y].typ == FOUNTAIN)
1508 Sprintf(tmpbuf, "<span class=\"nh_fountain\">%c</span>", c);
1509 else if (oclass < 0 && levl[x][y].typ == THRONE)
1510 Sprintf(tmpbuf, "<span class=\"nh_throne\">%c</span>", c);
1511 else if (oclass < 0 && levl[x][y].typ == SINK)
1512 Sprintf(tmpbuf, "<span class=\"nh_sink\">%c</span>", c);
1513 else if (oclass < 0 && levl[x][y].typ == GRAVE)
1514 Sprintf(tmpbuf, "<span class=\"nh_grave\">%c</span>", c);
1515 else if (oclass < 0 && levl[x][y].typ == ALTAR)
1516 Sprintf(tmpbuf, "<span class=\"nh_altar\">%c</span>", c);
1517 else if (oclass < 0 && levl[x][y].typ == ICE)
1518 Sprintf(tmpbuf, "<span class=\"nh_ice\">%c</span>", c);
1519 else
1520 Sprintf(tmpbuf, "<span class=\"nh_color_%d\">%s</span>", color, html_escape_character(c));
1521 Strcat(html_buf, tmpbuf);
1522 Sprintf(tmpbuf, "%c", c);
1523 Strcat(buf, tmpbuf);
1524
1525 if (c != ' ')
1526 lastc = x;
1527 }
1528 dump_html("<span class=\"nh_screen\">%s</span>\n", html_buf);
1529 buf[lastc] = '\0';
1530 dump_text("%s\n", buf);
1531 }
1532 dump("", "");
1533 bot1str(buf);
1534 ptr = (char *) compress_str((const char *) buf);
1535 dump("", ptr);
1536 bot2str(buf);
1537 dump("", buf);
1538 dump_html("</pre>\n", "");
1539 dump("", "");
1540 dump("", "");
1541 }
1542 #endif /* DUMP_LOG */
1543
1544 /*
1545 * back_to_glyph()
1546 *
1547 * Use the information in the rm structure at the given position to create
1548 * a glyph of a background.
1549 *
1550 * I had to add a field in the rm structure (horizontal) so that we knew
1551 * if open doors and secret doors were horizontal or vertical. Previously,
1552 * the screen symbol had the horizontal/vertical information set at
1553 * level generation time.
1554 *
1555 * I used the 'ladder' field (really doormask) for deciding if stairwells
1556 * were up or down. I didn't want to check the upstairs and dnstairs
1557 * variables.
1558 */
1559 int
back_to_glyph(x,y)1560 back_to_glyph(x,y)
1561 xchar x,y;
1562 {
1563 int idx;
1564 struct rm *ptr = &(levl[x][y]);
1565
1566 switch (ptr->typ) {
1567 case SCORR:
1568 case STONE:
1569 idx = level.flags.arboreal ? S_tree : S_stone;
1570 break;
1571 case ROOM:
1572 idx = (!cansee(x,y) && !ptr->waslit) ? S_darkroom : S_room;
1573 break;
1574 case CORR:
1575 idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1576 break;
1577 case HWALL:
1578 case VWALL:
1579 case TLCORNER:
1580 case TRCORNER:
1581 case BLCORNER:
1582 case BRCORNER:
1583 case CROSSWALL:
1584 case TUWALL:
1585 case TDWALL:
1586 case TLWALL:
1587 case TRWALL:
1588 case SDOOR:
1589 idx = ptr->seenv ? wall_angle(ptr) : S_stone;
1590 break;
1591 case DOOR:
1592 if (ptr->doormask) {
1593 if (ptr->doormask & D_BROKEN)
1594 idx = S_ndoor;
1595 else if (ptr->doormask & D_ISOPEN)
1596 idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
1597 else /* else is closed */
1598 idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
1599 } else
1600 idx = S_ndoor;
1601 break;
1602 case IRONBARS: idx = S_bars; break;
1603 case TREE: idx = S_tree; break;
1604 case POOL:
1605 case MOAT: idx = S_pool; break;
1606 case STAIRS:
1607 idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
1608 break;
1609 case LADDER:
1610 idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
1611 break;
1612 case FOUNTAIN: idx = S_fountain; break;
1613 case SINK: idx = S_sink; break;
1614 case ALTAR: idx = S_altar; break;
1615 case GRAVE: idx = S_grave; break;
1616 case THRONE: idx = S_throne; break;
1617 case LAVAPOOL: idx = S_lava; break;
1618 case ICE: idx = S_ice; break;
1619 case AIR: idx = S_air; break;
1620 case CLOUD: idx = S_cloud; break;
1621 case WATER: idx = S_water; break;
1622 case DBWALL:
1623 idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
1624 break;
1625 case DRAWBRIDGE_UP:
1626 switch(ptr->drawbridgemask & DB_UNDER) {
1627 case DB_MOAT: idx = S_pool; break;
1628 case DB_LAVA: idx = S_lava; break;
1629 case DB_ICE: idx = S_ice; break;
1630 case DB_FLOOR: idx = (!cansee(x,y) && !ptr->waslit) ? S_darkroom : S_room; break;
1631 default:
1632 impossible("Strange db-under: %d",
1633 ptr->drawbridgemask & DB_UNDER);
1634 idx = (!cansee(x,y) && !ptr->waslit) ? S_darkroom : S_room; /* something is better than nothing */
1635 break;
1636 }
1637 break;
1638 case DRAWBRIDGE_DOWN:
1639 idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
1640 break;
1641 default:
1642 impossible("back_to_glyph: unknown level type [ = %d ]",ptr->typ);
1643 idx = (!cansee(x,y) && !ptr->waslit) ? S_darkroom : S_room;
1644 break;
1645 }
1646
1647 return cmap_to_glyph(idx);
1648 }
1649
1650
1651 /*
1652 * swallow_to_glyph()
1653 *
1654 * Convert a monster number and a swallow location into the correct glyph.
1655 * If you don't want a patchwork monster while hallucinating, decide on
1656 * a random monster in swallowed() and don't use what_mon() here.
1657 */
1658 STATIC_OVL int
swallow_to_glyph(mnum,loc)1659 swallow_to_glyph(mnum, loc)
1660 int mnum;
1661 int loc;
1662 {
1663 if (loc < S_sw_tl || S_sw_br < loc) {
1664 impossible("swallow_to_glyph: bad swallow location");
1665 loc = S_sw_br;
1666 }
1667 return ((int) (what_mon(mnum)<<3) | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
1668 }
1669
1670
1671
1672 /*
1673 * zapdir_to_glyph()
1674 *
1675 * Change the given zap direction and beam type into a glyph. Each beam
1676 * type has four glyphs, one for each of the symbols below. The order of
1677 * the zap symbols [0-3] as defined in rm.h are:
1678 *
1679 * | S_vbeam ( 0, 1) or ( 0,-1)
1680 * - S_hbeam ( 1, 0) or (-1, 0)
1681 * \ S_lslant ( 1, 1) or (-1,-1)
1682 * / S_rslant (-1, 1) or ( 1,-1)
1683 */
1684 int
zapdir_to_glyph(dx,dy,beam_type)1685 zapdir_to_glyph(dx, dy, beam_type)
1686 register int dx, dy;
1687 int beam_type;
1688 {
1689 if (beam_type >= NUM_ZAP) {
1690 warning("zapdir_to_glyph: illegal beam type");
1691 beam_type = 0;
1692 }
1693 dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
1694
1695 return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
1696 }
1697
1698
1699 /*
1700 * Utility routine for dowhatis() used to find out the glyph displayed at
1701 * the location. This isn't necessarily the same as the glyph in the levl
1702 * structure, so we must check the "third screen".
1703 */
1704 int
glyph_at(x,y)1705 glyph_at(x, y)
1706 xchar x,y;
1707 {
1708 if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
1709 return cmap_to_glyph(S_room); /* XXX */
1710 return gbuf[y][x].glyph;
1711 }
1712
1713
1714 /* ------------------------------------------------------------------------- */
1715 /* Wall Angle -------------------------------------------------------------- */
1716
1717 /*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
1718
1719 #ifdef WA_VERBOSE
1720
1721 static const char *FDECL(type_to_name, (int));
1722 static void FDECL(error4, (int,int,int,int,int,int));
1723
1724 static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1725 static const char *type_names[MAX_TYPE] = {
1726 "STONE", "VWALL", "HWALL", "TLCORNER",
1727 "TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL",
1728 "TUWALL", "TDWALL", "TLWALL", "TRWALL",
1729 "DBWALL", "SDOOR", "SCORR", "POOL",
1730 "MOAT", "WATER", "DRAWBRIDGE_UP","LAVAPOOL",
1731 "DOOR", "CORR", "ROOM", "STAIRS",
1732 "LADDER", "FOUNTAIN", "THRONE", "SINK",
1733 "ALTAR", "ICE", "DRAWBRIDGE_DOWN","AIR",
1734 "CLOUD"
1735 };
1736
1737
1738 static const char *
type_to_name(type)1739 type_to_name(type)
1740 int type;
1741 {
1742 return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type];
1743 }
1744
1745 static void
error4(x,y,a,b,c,dd)1746 error4(x, y, a, b, c, dd)
1747 int x, y, a, b, c, dd;
1748 {
1749 pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1750 type_to_name(levl[x][y].typ), x, y,
1751 a ? "1":"", b ? "2":"", c ? "3":"", dd ? "4":"");
1752 bad_count[levl[x][y].typ]++;
1753 }
1754 #endif /* WA_VERBOSE */
1755
1756 /*
1757 * Return 'which' if position is implies an unfinshed exterior. Return
1758 * zero otherwise. Unfinished implies outer area is rock or a corridor.
1759 *
1760 * Things that are ambigious: lava
1761 */
1762 STATIC_OVL int
check_pos(x,y,which)1763 check_pos(x, y, which)
1764 int x, y, which;
1765 {
1766 int type;
1767 if (!isok(x,y)) return which;
1768 type = levl[x][y].typ;
1769 if (IS_ROCK(type) || type == CORR || type == SCORR) return which;
1770 return 0;
1771 }
1772
1773 /* Return TRUE if more than one is non-zero. */
1774 /*ARGSUSED*/
1775 #ifdef WA_VERBOSE
1776 STATIC_OVL boolean
more_than_one(x,y,a,b,c)1777 more_than_one(x, y, a, b, c)
1778 int x, y, a, b, c;
1779 {
1780 if ((a && (b|c)) || (b && (a|c)) || (c && (a|b))) {
1781 error4(x,y,a,b,c,0);
1782 return TRUE;
1783 }
1784 return FALSE;
1785 }
1786 #else
1787 #define more_than_one(x, y, a, b, c) (((a) && ((b)|(c))) || ((b) && ((a)|(c))) || ((c) && ((a)|(b))))
1788 #endif
1789
1790 /* Return the wall mode for a T wall. */
1791 STATIC_OVL int
set_twall(x0,y0,x1,y1,x2,y2,x3,y3)1792 set_twall(x0,y0, x1,y1, x2,y2, x3,y3)
1793 int x0,y0, x1,y1, x2,y2, x3,y3;
1794 {
1795 int wmode, is_1, is_2, is_3;
1796
1797 is_1 = check_pos(x1, y1, WM_T_LONG);
1798 is_2 = check_pos(x2, y2, WM_T_BL);
1799 is_3 = check_pos(x3, y3, WM_T_BR);
1800 if (more_than_one(x0, y0, is_1, is_2, is_3)) {
1801 wmode = 0;
1802 } else {
1803 wmode = is_1 + is_2 + is_3;
1804 }
1805 return wmode;
1806 }
1807
1808 /* Return wall mode for a horizontal or vertical wall. */
1809 STATIC_OVL int
set_wall(x,y,horiz)1810 set_wall(x, y, horiz)
1811 int x, y, horiz;
1812 {
1813 int wmode, is_1, is_2;
1814
1815 if (horiz) {
1816 is_1 = check_pos(x,y-1, WM_W_TOP);
1817 is_2 = check_pos(x,y+1, WM_W_BOTTOM);
1818 } else {
1819 is_1 = check_pos(x-1,y, WM_W_LEFT);
1820 is_2 = check_pos(x+1,y, WM_W_RIGHT);
1821 }
1822 if (more_than_one(x, y, is_1, is_2, 0)) {
1823 wmode = 0;
1824 } else {
1825 wmode = is_1 + is_2;
1826 }
1827 return wmode;
1828 }
1829
1830
1831 /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
1832 STATIC_OVL int
set_corn(x1,y1,x2,y2,x3,y3,x4,y4)1833 set_corn(x1,y1, x2,y2, x3,y3, x4,y4)
1834 int x1, y1, x2, y2, x3, y3, x4, y4;
1835 {
1836 int wmode, is_1, is_2, is_3, is_4;
1837
1838 is_1 = check_pos(x1, y1, 1);
1839 is_2 = check_pos(x2, y2, 1);
1840 is_3 = check_pos(x3, y3, 1);
1841 is_4 = check_pos(x4, y4, 1); /* inner location */
1842
1843 /*
1844 * All 4 should not be true. So if the inner location is rock,
1845 * use it. If all of the outer 3 are true, use outer. We currently
1846 * can't cover the case where only part of the outer is rock, so
1847 * we just say that all the walls are finished (if not overridden
1848 * by the inner section).
1849 */
1850 if (is_4) {
1851 wmode = WM_C_INNER;
1852 } else if (is_1 && is_2 && is_3)
1853 wmode = WM_C_OUTER;
1854 else
1855 wmode = 0; /* finished walls on all sides */
1856
1857 return wmode;
1858 }
1859
1860 /* Return mode for a crosswall. */
1861 STATIC_OVL int
set_crosswall(x,y)1862 set_crosswall(x, y)
1863 int x, y;
1864 {
1865 int wmode, is_1, is_2, is_3, is_4;
1866
1867 is_1 = check_pos(x-1, y-1, 1);
1868 is_2 = check_pos(x+1, y-1, 1);
1869 is_3 = check_pos(x+1, y+1, 1);
1870 is_4 = check_pos(x-1, y+1, 1);
1871
1872 wmode = is_1+is_2+is_3+is_4;
1873 if (wmode > 1) {
1874 if (is_1 && is_3 && (is_2+is_4 == 0)) {
1875 wmode = WM_X_TLBR;
1876 } else if (is_2 && is_4 && (is_1+is_3 == 0)) {
1877 wmode = WM_X_BLTR;
1878 } else {
1879 #ifdef WA_VERBOSE
1880 error4(x,y,is_1,is_2,is_3,is_4);
1881 #endif
1882 wmode = 0;
1883 }
1884 } else if (is_1)
1885 wmode = WM_X_TL;
1886 else if (is_2)
1887 wmode = WM_X_TR;
1888 else if (is_3)
1889 wmode = WM_X_BR;
1890 else if (is_4)
1891 wmode = WM_X_BL;
1892
1893 return wmode;
1894 }
1895
1896 /* Called from mklev. Scan the level and set the wall modes. */
1897 void
set_wall_state()1898 set_wall_state()
1899 {
1900 int x, y;
1901 int wmode;
1902 struct rm *lev;
1903
1904 #ifdef WA_VERBOSE
1905 for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0;
1906 #endif
1907
1908 for (x = 0; x < COLNO; x++)
1909 for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
1910 switch (lev->typ) {
1911 case SDOOR:
1912 wmode = set_wall(x, y, (int) lev->horizontal);
1913 break;
1914 case VWALL:
1915 wmode = set_wall(x, y, 0);
1916 break;
1917 case HWALL:
1918 wmode = set_wall(x, y, 1);
1919 break;
1920 case TDWALL:
1921 wmode = set_twall(x,y, x,y-1, x-1,y+1, x+1,y+1);
1922 break;
1923 case TUWALL:
1924 wmode = set_twall(x,y, x,y+1, x+1,y-1, x-1,y-1);
1925 break;
1926 case TLWALL:
1927 wmode = set_twall(x,y, x+1,y, x-1,y-1, x-1,y+1);
1928 break;
1929 case TRWALL:
1930 wmode = set_twall(x,y, x-1,y, x+1,y+1, x+1,y-1);
1931 break;
1932 case TLCORNER:
1933 wmode = set_corn(x-1,y-1, x,y-1, x-1,y, x+1,y+1);
1934 break;
1935 case TRCORNER:
1936 wmode = set_corn(x,y-1, x+1,y-1, x+1,y, x-1,y+1);
1937 break;
1938 case BLCORNER:
1939 wmode = set_corn(x,y+1, x-1,y+1, x-1,y, x+1,y-1);
1940 break;
1941 case BRCORNER:
1942 wmode = set_corn(x+1,y, x+1,y+1, x,y+1, x-1,y-1);
1943 break;
1944 case CROSSWALL:
1945 wmode = set_crosswall(x, y);
1946 break;
1947
1948 default:
1949 wmode = -1; /* don't set wall info */
1950 break;
1951 }
1952
1953 if (wmode >= 0)
1954 lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
1955 }
1956
1957 #ifdef WA_VERBOSE
1958 /* check if any bad positions found */
1959 for (x = y = 0; x < MAX_TYPE; x++)
1960 if (bad_count[x]) {
1961 if (y == 0) {
1962 y = 1; /* only print once */
1963 pline("set_wall_type: wall mode problems with: ");
1964 }
1965 pline("%s %d;", type_names[x], bad_count[x]);
1966 }
1967 #endif /* WA_VERBOSE */
1968 }
1969
1970 /* ------------------------------------------------------------------------- */
1971 /* This matrix is used here and in vision.c. */
1972 unsigned char seenv_matrix[3][3] = { {SV2, SV1, SV0},
1973 {SV3, SVALL, SV7},
1974 {SV4, SV5, SV6} };
1975
1976 #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
1977
1978 /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
1979 STATIC_OVL void
set_seenv(lev,x0,y0,x,y)1980 set_seenv(lev, x0, y0, x, y)
1981 struct rm *lev;
1982 int x0, y0, x, y; /* from, to */
1983 {
1984 int dx = x-x0, dy = y0-y;
1985 lev->seenv |= seenv_matrix[sign(dy)+1][sign(dx)+1];
1986 }
1987
1988 /* ------------------------------------------------------------------------- */
1989
1990 /* T wall types, one for each row in wall_matrix[][]. */
1991 #define T_d 0
1992 #define T_l 1
1993 #define T_u 2
1994 #define T_r 3
1995
1996 /*
1997 * These are the column names of wall_matrix[][]. They are the "results"
1998 * of a tdwall pattern match. All T walls are rotated so they become
1999 * a tdwall. Then we do a single pattern match, but return the
2000 * correct result for the original wall by using different rows for
2001 * each of the wall types.
2002 */
2003 #define T_stone 0
2004 #define T_tlcorn 1
2005 #define T_trcorn 2
2006 #define T_hwall 3
2007 #define T_tdwall 4
2008
2009 static const int wall_matrix[4][5] = {
2010 { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
2011 { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
2012 { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
2013 { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
2014 };
2015
2016
2017 /* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][]. */
2018 #define C_bl 0
2019 #define C_tl 1
2020 #define C_tr 2
2021 #define C_br 3
2022
2023 /*
2024 * These are the column names for cross_matrix[][]. They express results
2025 * in C_br (bottom right) terms. All crosswalls with a single solid
2026 * quarter are rotated so the solid section is at the bottom right.
2027 * We pattern match on that, but return the correct result depending
2028 * on which row we'ere looking at.
2029 */
2030 #define C_trcorn 0
2031 #define C_brcorn 1
2032 #define C_blcorn 2
2033 #define C_tlwall 3
2034 #define C_tuwall 4
2035 #define C_crwall 5
2036
2037 static const int cross_matrix[4][6] = {
2038 { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
2039 { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
2040 { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
2041 { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
2042 };
2043
2044
2045 /* Print out a T wall warning and all interesting info. */
2046 STATIC_OVL void
t_warn(lev)2047 t_warn(lev)
2048 struct rm *lev;
2049 {
2050 static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
2051 const char *wname;
2052
2053 if (lev->typ == TUWALL) wname = "tuwall";
2054 else if (lev->typ == TLWALL) wname = "tlwall";
2055 else if (lev->typ == TRWALL) wname = "trwall";
2056 else if (lev->typ == TDWALL) wname = "tdwall";
2057 else wname = "unknown";
2058 impossible(warn_str, wname, lev->wall_info & WM_MASK,
2059 (unsigned int) lev->seenv);
2060 }
2061
2062
2063 /*
2064 * Return the correct graphics character index using wall type, wall mode,
2065 * and the seen vector. It is expected that seenv is non zero.
2066 *
2067 * All T-wall vectors are rotated to be TDWALL. All single crosswall
2068 * blocks are rotated to bottom right. All double crosswall are rotated
2069 * to W_X_BLTR. All results are converted back.
2070 *
2071 * The only way to understand this is to take out pen and paper and
2072 * draw diagrams. See rm.h for more details on the wall modes and
2073 * seen vector (SV).
2074 */
2075 STATIC_OVL int
wall_angle(lev)2076 wall_angle(lev)
2077 struct rm *lev;
2078 {
2079 register unsigned int seenv = lev->seenv & 0xff;
2080 const int *row;
2081 int col, idx;
2082
2083 #define only(sv, bits) (((sv) & (bits)) && ! ((sv) & ~(bits)))
2084 switch (lev->typ) {
2085 case TUWALL:
2086 row = wall_matrix[T_u];
2087 seenv = (seenv >> 4 | seenv << 4) & 0xff;/* rotate to tdwall */
2088 goto do_twall;
2089 case TLWALL:
2090 row = wall_matrix[T_l];
2091 seenv = (seenv >> 2 | seenv << 6) & 0xff;/* rotate to tdwall */
2092 goto do_twall;
2093 case TRWALL:
2094 row = wall_matrix[T_r];
2095 seenv = (seenv >> 6 | seenv << 2) & 0xff;/* rotate to tdwall */
2096 goto do_twall;
2097 case TDWALL:
2098 row = wall_matrix[T_d];
2099 do_twall:
2100 switch (lev->wall_info & WM_MASK) {
2101 case 0:
2102 if (seenv == SV4) {
2103 col = T_tlcorn;
2104 } else if (seenv == SV6) {
2105 col = T_trcorn;
2106 } else if (seenv & (SV3|SV5|SV7) ||
2107 ((seenv & SV4) && (seenv & SV6))) {
2108 col = T_tdwall;
2109 } else if (seenv & (SV0|SV1|SV2)) {
2110 col = (seenv & (SV4|SV6) ? T_tdwall : T_hwall);
2111 } else {
2112 t_warn(lev);
2113 col = T_stone;
2114 }
2115 break;
2116 case WM_T_LONG:
2117 if (seenv & (SV3|SV4) && !(seenv & (SV5|SV6|SV7))) {
2118 col = T_tlcorn;
2119 } else if (seenv&(SV6|SV7) && !(seenv&(SV3|SV4|SV5))) {
2120 col = T_trcorn;
2121 } else if ((seenv & SV5) ||
2122 ((seenv & (SV3|SV4)) && (seenv & (SV6|SV7)))) {
2123 col = T_tdwall;
2124 } else {
2125 /* only SV0|SV1|SV2 */
2126 if (! only(seenv, SV0|SV1|SV2) )
2127 t_warn(lev);
2128 col = T_stone;
2129 }
2130 break;
2131 case WM_T_BL:
2132 #if 0 /* older method, fixed */
2133 if (only(seenv, SV4|SV5)) {
2134 col = T_tlcorn;
2135 } else if ((seenv & (SV0|SV1|SV2)) &&
2136 only(seenv, SV0|SV1|SV2|SV6|SV7)) {
2137 col = T_hwall;
2138 } else if (seenv & SV3 ||
2139 ((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) {
2140 col = T_tdwall;
2141 } else {
2142 if (seenv != SV6)
2143 t_warn(lev);
2144 col = T_stone;
2145 }
2146 #endif /* 0 */
2147 if (only(seenv, SV4|SV5))
2148 col = T_tlcorn;
2149 else if ((seenv & (SV0|SV1|SV2|SV7)) &&
2150 !(seenv & (SV3|SV4|SV5)))
2151 col = T_hwall;
2152 else if (only(seenv, SV6))
2153 col = T_stone;
2154 else
2155 col = T_tdwall;
2156 break;
2157 case WM_T_BR:
2158 #if 0 /* older method, fixed */
2159 if (only(seenv, SV5|SV6)) {
2160 col = T_trcorn;
2161 } else if ((seenv & (SV0|SV1|SV2)) &&
2162 only(seenv, SV0|SV1|SV2|SV3|SV4)) {
2163 col = T_hwall;
2164 } else if (seenv & SV7 ||
2165 ((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) {
2166 col = T_tdwall;
2167 } else {
2168 if (seenv != SV4)
2169 t_warn(lev);
2170 col = T_stone;
2171 }
2172 #endif /* 0 */
2173 if (only(seenv, SV5|SV6))
2174 col = T_trcorn;
2175 else if ((seenv & (SV0|SV1|SV2|SV3)) &&
2176 !(seenv & (SV5|SV6|SV7)))
2177 col = T_hwall;
2178 else if (only(seenv, SV4))
2179 col = T_stone;
2180 else
2181 col = T_tdwall;
2182
2183 break;
2184 default:
2185 impossible("wall_angle: unknown T wall mode %d",
2186 lev->wall_info & WM_MASK);
2187 col = T_stone;
2188 break;
2189 }
2190 idx = row[col];
2191 break;
2192
2193 case SDOOR:
2194 if (lev->horizontal) goto horiz;
2195 /* fall through */
2196 case VWALL:
2197 switch (lev->wall_info & WM_MASK) {
2198 case 0: idx = seenv ? S_vwall : S_stone; break;
2199 case 1: idx = seenv & (SV1|SV2|SV3|SV4|SV5) ? S_vwall :
2200 S_stone;
2201 break;
2202 case 2: idx = seenv & (SV0|SV1|SV5|SV6|SV7) ? S_vwall :
2203 S_stone;
2204 break;
2205 default:
2206 impossible("wall_angle: unknown vwall mode %d",
2207 lev->wall_info & WM_MASK);
2208 idx = S_stone;
2209 break;
2210 }
2211 break;
2212
2213 case HWALL:
2214 horiz:
2215 switch (lev->wall_info & WM_MASK) {
2216 case 0: idx = seenv ? S_hwall : S_stone; break;
2217 case 1: idx = seenv & (SV3|SV4|SV5|SV6|SV7) ? S_hwall :
2218 S_stone;
2219 break;
2220 case 2: idx = seenv & (SV0|SV1|SV2|SV3|SV7) ? S_hwall :
2221 S_stone;
2222 break;
2223 default:
2224 impossible("wall_angle: unknown hwall mode %d",
2225 lev->wall_info & WM_MASK);
2226 idx = S_stone;
2227 break;
2228 }
2229 break;
2230
2231 #define set_corner(idx, lev, which, outer, inner, name) \
2232 switch ((lev)->wall_info & WM_MASK) { \
2233 case 0: idx = which; break; \
2234 case WM_C_OUTER: idx = seenv & (outer) ? which : S_stone; break; \
2235 case WM_C_INNER: idx = seenv & ~(inner) ? which : S_stone; break; \
2236 default: \
2237 impossible("wall_angle: unknown %s mode %d", name, \
2238 (lev)->wall_info & WM_MASK); \
2239 idx = S_stone; \
2240 break; \
2241 }
2242
2243 case TLCORNER:
2244 set_corner(idx, lev, S_tlcorn, (SV3|SV4|SV5), SV4, "tlcorn");
2245 break;
2246 case TRCORNER:
2247 set_corner(idx, lev, S_trcorn, (SV5|SV6|SV7), SV6, "trcorn");
2248 break;
2249 case BLCORNER:
2250 set_corner(idx, lev, S_blcorn, (SV1|SV2|SV3), SV2, "blcorn");
2251 break;
2252 case BRCORNER:
2253 set_corner(idx, lev, S_brcorn, (SV7|SV0|SV1), SV0, "brcorn");
2254 break;
2255
2256
2257 case CROSSWALL:
2258 switch (lev->wall_info & WM_MASK) {
2259 case 0:
2260 if (seenv == SV0)
2261 idx = S_brcorn;
2262 else if (seenv == SV2)
2263 idx = S_blcorn;
2264 else if (seenv == SV4)
2265 idx = S_tlcorn;
2266 else if (seenv == SV6)
2267 idx = S_trcorn;
2268 else if (!(seenv & ~(SV0|SV1|SV2)) &&
2269 (seenv & SV1 || seenv == (SV0|SV2)))
2270 idx = S_tuwall;
2271 else if (!(seenv & ~(SV2|SV3|SV4)) &&
2272 (seenv & SV3 || seenv == (SV2|SV4)))
2273 idx = S_trwall;
2274 else if (!(seenv & ~(SV4|SV5|SV6)) &&
2275 (seenv & SV5 || seenv == (SV4|SV6)))
2276 idx = S_tdwall;
2277 else if (!(seenv & ~(SV0|SV6|SV7)) &&
2278 (seenv & SV7 || seenv == (SV0|SV6)))
2279 idx = S_tlwall;
2280 else
2281 idx = S_crwall;
2282 break;
2283
2284 case WM_X_TL:
2285 row = cross_matrix[C_tl];
2286 seenv = (seenv >> 4 | seenv << 4) & 0xff;
2287 goto do_crwall;
2288 case WM_X_TR:
2289 row = cross_matrix[C_tr];
2290 seenv = (seenv >> 6 | seenv << 2) & 0xff;
2291 goto do_crwall;
2292 case WM_X_BL:
2293 row = cross_matrix[C_bl];
2294 seenv = (seenv >> 2 | seenv << 6) & 0xff;
2295 goto do_crwall;
2296 case WM_X_BR:
2297 row = cross_matrix[C_br];
2298 do_crwall:
2299 if (seenv == SV4)
2300 idx = S_stone;
2301 else {
2302 seenv = seenv & ~SV4; /* strip SV4 */
2303 if (seenv == SV0) {
2304 col = C_brcorn;
2305 } else if (seenv & (SV2|SV3)) {
2306 if (seenv & (SV5|SV6|SV7))
2307 col = C_crwall;
2308 else if (seenv & (SV0|SV1))
2309 col = C_tuwall;
2310 else
2311 col = C_blcorn;
2312 } else if (seenv & (SV5|SV6)) {
2313 if (seenv & (SV1|SV2|SV3))
2314 col = C_crwall;
2315 else if (seenv & (SV0|SV7))
2316 col = C_tlwall;
2317 else
2318 col = C_trcorn;
2319 } else if (seenv & SV1) {
2320 col = seenv & SV7 ? C_crwall : C_tuwall;
2321 } else if (seenv & SV7) {
2322 col = seenv & SV1 ? C_crwall : C_tlwall;
2323 } else {
2324 impossible(
2325 "wall_angle: bottom of crwall check");
2326 col = C_crwall;
2327 }
2328
2329 idx = row[col];
2330 }
2331 break;
2332
2333 case WM_X_TLBR:
2334 if ( only(seenv, SV1|SV2|SV3) )
2335 idx = S_blcorn;
2336 else if ( only(seenv, SV5|SV6|SV7) )
2337 idx = S_trcorn;
2338 else if ( only(seenv, SV0|SV4) )
2339 idx = S_stone;
2340 else
2341 idx = S_crwall;
2342 break;
2343
2344 case WM_X_BLTR:
2345 if ( only(seenv, SV0|SV1|SV7) )
2346 idx = S_brcorn;
2347 else if ( only(seenv, SV3|SV4|SV5) )
2348 idx = S_tlcorn;
2349 else if ( only(seenv, SV2|SV6) )
2350 idx = S_stone;
2351 else
2352 idx = S_crwall;
2353 break;
2354
2355 default:
2356 impossible("wall_angle: unknown crosswall mode");
2357 idx = S_stone;
2358 break;
2359 }
2360 break;
2361
2362 default:
2363 impossible("wall_angle: unexpected wall type %d", lev->typ);
2364 idx = S_stone;
2365 }
2366 return idx;
2367 }
2368
2369 /*display.c*/
2370