1 | /* SCCS Id: @(#)display.c 3.3 2000/07/27 */
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 *
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
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_stone);
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 | }
190 |
191 | /*
192 | * The routines map_background(), map_object(), and map_trap() could just
193 | * as easily be:
194 | *
195 | * map_glyph(x,y,glyph,show)
196 | *
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
200 | * make this change.
201 | */
202 | /* FIXME: some of these use xchars for x and y, and some use ints. Make
203 | * this consistent.
204 | */
205 |
206 | /*
207 | * map_background()
208 | *
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.
211 | */
212 | void
213 | map_background(x, y, show)
214 | register xchar x,y;
215 | register int show;
216 | {
217 | register int glyph = back_to_glyph(x,y);
218 |
219 | if (level.flags.hero_memory)
220 | levl[x][y].glyph = glyph;
221 | if (show) show_glyph(x,y, glyph);
222 | }
223 |
224 | /*
225 | * map_trap()
226 | *
227 | * Map the trap and print it out if directed. This routine assumes that the
228 | * hero can physically see the location.
229 | */
230 | void
231 | map_trap(trap, show)
232 | register struct trap *trap;
233 | register int show;
234 | {
235 | register int x = trap->tx, y = trap->ty;
236 | register int glyph = trap_to_glyph(trap);
237 |
238 | if (level.flags.hero_memory)
239 | levl[x][y].glyph = glyph;
240 | if (show) show_glyph(x, y, glyph);
241 | }
242 |
243 | /*
244 | * map_object()
245 | *
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.
248 | */
249 | void
250 | map_object(obj, show)
251 | register struct obj *obj;
252 | register int show;
253 | {
254 | register int x = obj->ox, y = obj->oy;
255 | register int glyph = obj_to_glyph(obj);
256 |
257 | if (level.flags.hero_memory)
258 | levl[x][y].glyph = glyph;
259 | if (show) show_glyph(x, y, glyph);
260 | }
261 |
262 | /*
263 | * map_invisible()
264 | *
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.
270 | */
271 | void
272 | map_invisible(x, y)
273 | register xchar x, y;
274 | {
275 | if (level.flags.hero_memory)
276 | levl[x][y].glyph = GLYPH_INVISIBLE;
277 | show_glyph(x, y, GLYPH_INVISIBLE);
278 | }
279 |
280 | /*
281 | * unmap_object()
282 | *
283 | * Remove something from the map when the hero realizes it's not there any
284 | * more. Replace it with background or known trap, but not with any other
285 | * If this is used for detection, a full screen update is imminent anyway;
286 | * if this is used to get rid of an invisible monster notation, we might have
287 | * to call newsym().
288 | */
289 | void
290 | unmap_object(x, y)
291 | register int x, y;
292 | {
293 | register struct trap *trap;
294 |
295 | if (!level.flags.hero_memory) return;
296 |
297 | if ((trap = t_at(x,y)) != 0 && trap->tseen && !covers_traps(x,y))
298 | map_trap(trap, 0);
299 | else if (levl[x][y].seenv) {
300 | struct rm *lev = &levl[x][y];
301 |
302 | map_background(x, y, 0);
303 |
304 | /* turn remembered dark room squares dark */
305 | if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) &&
306 | lev->typ == ROOM)
307 | lev->glyph = cmap_to_glyph(S_stone);
308 | } else
309 | levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */
310 | }
311 |
312 |
313 | /*
314 | * map_location()
315 | *
316 | * Make whatever at this location show up. This is only for non-living
317 | * things. This will not handle feeling invisible objects correctly.
318 | *
319 | * Internal to display.c, this is a #define for speed.
320 | */
321 | #define _map_location(x,y,show) \
322 | { \
323 | register struct obj *obj; \
324 | register struct trap *trap; \
325 | \
326 | if ((obj = vobj_at(x,y)) && !covers_objects(x,y)) \
327 | map_object(obj,show); \
328 | else if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y)) \
329 | map_trap(trap,show); \
330 | else \
331 | map_background(x,y,show); \
332 | }
333 |
334 | void map_location(x,y,show)
335 | int x, y, show;
336 | {
337 | _map_location(x,y,show);
338 | }
339 |
340 |
341 | /*
342 | * display_monster()
343 | *
344 | * Note that this is *not* a map_XXXX() function! Monsters sort of float
345 | * above everything.
346 | *
347 | * Yuck. Display body parts by recognizing that the display position is
348 | * not the same as the monster position. Currently the only body part is
349 | * a worm tail.
350 | *
351 | */
352 | STATIC_OVL void
353 | display_monster(x, y, mon, in_sight, worm_tail)
354 | register xchar x, y; /* display position */
355 | register struct monst *mon; /* monster to display */
356 | int in_sight; /* TRUE if the monster is physically seen */
357 | register xchar worm_tail; /* mon is actually a worm tail */
358 | {
359 | register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING);
360 | register int sensed = mon_mimic &&
361 | (Protection_from_shape_changers || sensemon(mon));
362 | /*
363 | * We must do the mimic check first. If the mimic is mimicing something,
364 | * and the location is in sight, we have to change the hero's memory
365 | * so that when the position is out of sight, the hero remembers what
366 | * the mimic was mimicing.
367 | */
368 |
369 | if (mon_mimic && in_sight) {
370 | switch (mon->m_ap_type) {
371 | default:
372 | impossible("display_monster: bad m_ap_type value [ = %d ]",
373 | (int) mon->m_ap_type);
374 | case M_AP_NOTHING:
375 | show_glyph(x, y, mon_to_glyph(mon));
376 | break;
377 |
378 | case M_AP_FURNITURE: {
379 | /*
380 | * This is a poor man's version of map_background(). I can't
381 | * use map_background() because we are overriding what is in
382 | * the 'typ' field. Maybe have map_background()'s parameters
383 | * be (x,y,glyph) instead of just (x,y).
384 | *
385 | * mappearance is currently set to an S_ index value in
386 | * makemon.c.
387 | */
388 | register int glyph = cmap_to_glyph(mon->mappearance);
389 | levl[x][y].glyph = glyph;
390 | if (!sensed) show_glyph(x,y, glyph);
391 | break;
392 | }
393 |
394 | case M_AP_OBJECT: {
395 | struct obj obj; /* Make a fake object to send */
396 | /* to map_object(). */
397 | obj.ox = x;
398 | obj.oy = y;
399 | obj.otyp = mon->mappearance;
400 | obj.corpsenm = PM_TENGU; /* if mimicing a corpse */
401 | map_object(&obj,!sensed);
402 | break;
403 | }
404 |
405 | case M_AP_MONSTER:
406 | show_glyph(x,y, monnum_to_glyph(what_mon(mon->mappearance)));
407 | break;
408 | }
409 |
410 | }
411 |
412 | /* If the mimic is unsucessfully mimicing something, display the monster */
413 | if (!mon_mimic || sensed) {
414 | int num;
415 |
416 | if (Detect_monsters) {
417 | if (worm_tail)
418 | num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
419 | else
420 | num = detected_mon_to_glyph(mon);
421 | } else if (mon->mtame && !Hallucination) {
422 | if (worm_tail)
423 | num = petnum_to_glyph(PM_LONG_WORM_TAIL);
424 | else
425 | num = pet_to_glyph(mon);
426 | } else {
427 | if (worm_tail)
428 | num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
429 | else
430 | num = mon_to_glyph(mon);
431 | }
432 | show_glyph(x,y,num);
433 | }
434 | }
435 |
436 | /*
437 | * display_warning()
438 | *
439 | * This is also *not* a map_XXXX() function! Monster warnings float
440 | * above everything just like monsters do, but only if the monster
441 | * is not showing.
442 | *
443 | * Do not call for worm tails.
444 | */
445 | STATIC_OVL void
446 | display_warning(mon)
447 | register struct monst *mon;
448 | {
449 | int x = mon->mx, y = mon->my;
450 | int wl = (int) (mon->m_lev / 4);
451 | int glyph;
452 |
453 | if (mon_warning(mon)) {
454 | if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1;
455 | glyph = warning_to_glyph(wl);
456 | } else if (MATCH_WARN_OF_MON(mon)) {
457 | glyph = mon_to_glyph(mon);
458 | } else {
459 | impossible("display_warning did not match warning type?");
460 | return;
461 | }
462 | show_glyph(x, y, glyph);
463 | }
464 |
465 | /*
466 | * feel_location()
467 | *
468 | * Feel the given location. This assumes that the hero is blind and that
469 | * the given position is either the hero's or one of the eight squares
470 | * adjacent to the hero (except for a boulder push).
471 | * If an invisible monster has gone away, that will be discovered. If an
472 | * invisible monster has appeared, this will _not_ be discovered since
473 | * searching only finds one monster per turn so we must check that separately.
474 | */
475 | void
476 | feel_location(x, y)
477 | xchar x, y;
478 | {
479 | struct rm *lev = &(levl[x][y]);
480 | struct obj *boulder;
481 | register struct monst *mon;
482 |
483 | /* If the hero's memory of an invisible monster is accurate, we want to keep
484 | * him from detecting the same monster over and over again on each turn.
485 | * We must return (so we don't erase the monster). (We must also, in the
486 | * search function, be sure to skip over previously detected 'I's.)
487 | */
488 | if (glyph_is_invisible(levl[x][y].glyph) && m_at(x,y)) return;
489 |
490 | /* The hero can't feel non pool locations while under water. */
491 | if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(x,y))
492 | return;
493 |
494 | /* Set the seen vector as if the hero had seen it. It doesn't matter */
495 | /* if the hero is levitating or not. */
496 | set_seenv(lev, u.ux, u.uy, x, y);
497 |
498 | if (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) {
499 | /*
500 | * Levitation Rules. It is assumed that the hero can feel the state
501 | * of the walls around herself and can tell if she is in a corridor,
502 | * room, or doorway. Boulders are felt because they are large enough.
503 | * Anything else is unknown because the hero can't reach the ground.
504 | * This makes things difficult.
505 | *
506 | * Check (and display) in order:
507 | *
508 | * + Stone, walls, and closed doors.
509 | * + Boulders. [see a boulder before a doorway]
510 | * + Doors.
511 | * + Room/water positions
512 | * + Everything else (hallways!)
513 | */
514 | if (IS_ROCK(lev->typ) || (IS_DOOR(lev->typ) &&
515 | (lev->doormask & (D_LOCKED | D_CLOSED)))) {
516 | map_background(x, y, 1);
517 | } else if ((boulder = sobj_at(BOULDER,x,y)) != 0) {
518 | map_object(boulder, 1);
519 | } else if (IS_DOOR(lev->typ)) {
520 | map_background(x, y, 1);
521 | } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) {
522 | /*
523 | * An open room or water location. Normally we wouldn't touch
524 | * this, but we have to get rid of remembered boulder symbols.
525 | * This will only occur in rare occations when the hero goes
526 | * blind and doesn't find a boulder where expected (something
527 | * came along and picked it up). We know that there is not a
528 | * boulder at this location. Show fountains, pools, etc.
529 | * underneath if already seen. Otherwise, show the appropriate
530 | * floor symbol.
531 | *
532 | * This isn't quite correct. If the boulder was on top of some
533 | * other objects they should be seen once the boulder is removed.
534 | * However, we have no way of knowing that what is there now
535 | * was there then. So we let the hero have a lapse of memory.
536 | * We could also just display what is currently on the top of the
537 | * object stack (if anything).
538 | */
539 | if (lev->glyph == objnum_to_glyph(BOULDER)) {
540 | if (lev->typ != ROOM && lev->seenv) {
541 | map_background(x, y, 1);
542 | } else {
543 | lev->glyph = lev->waslit ? cmap_to_glyph(S_room) :
544 | cmap_to_glyph(S_stone);
545 | show_glyph(x,y,lev->glyph);
546 | }
547 | }
548 | } else {
549 | /* We feel it (I think hallways are the only things left). */
550 | map_background(x, y, 1);
551 | /* Corridors are never felt as lit (unless remembered that way) */
552 | /* (lit_corridor only). */
553 | if (lev->typ == CORR &&
554 | lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit)
555 | show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
556 | }
557 | } else {
558 | _map_location(x, y, 1);
559 |
560 | if (Punished) {
561 | /*
562 | * A ball or chain is only felt if it is first on the object
563 | * location list. Otherwise, we need to clear the felt bit ---
564 | * something has been dropped on the ball/chain. If the bit is
565 | * not cleared, then when the ball/chain is moved it will drop
566 | * the wrong glyph.
567 | */
568 | if (uchain->ox == x && uchain->oy == y) {
569 | if (level.objects[x][y] == uchain)
570 | u.bc_felt |= BC_CHAIN;
571 | else
572 | u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */
573 | }
574 | if (!carried(uball) && uball->ox == x && uball->oy == y) {
575 | if (level.objects[x][y] == uball)
576 | u.bc_felt |= BC_BALL;
577 | else
578 | u.bc_felt &= ~BC_BALL; /* do not feel the ball */
579 | }
580 | }
581 |
582 | /* Floor spaces are dark if unlit. Corridors are dark if unlit. */
583 | if (lev->typ == ROOM &&
584 | lev->glyph == cmap_to_glyph(S_room) && !lev->waslit)
585 | show_glyph(x,y, lev->glyph = cmap_to_glyph(S_stone));
586 | else if (lev->typ == CORR &&
587 | lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit)
588 | show_glyph(x,y, lev->glyph = cmap_to_glyph(S_corr));
589 | }
590 | /* draw monster on top if we can sense it */
591 | if ((x != u.ux || y != u.uy) && (mon = m_at(x,y)) && sensemon(mon))
592 | display_monster(x,y,mon,1,((x != mon->mx) || (y != mon->my)));
593 | }
594 |
595 | /*
596 | * newsym()
597 | *
598 | * Possibly put a new glyph at the given location.
599 | */
600 | void
601 | newsym(x,y)
602 | register int x,y;
603 | {
604 | register struct monst *mon;
605 | register struct rm *lev = &(levl[x][y]);
606 | register int see_it;
607 | register xchar worm_tail;
608 |
609 | if (in_mklev) return;
610 |
611 | /* only permit updating the hero when swallowed */
612 | if (u.uswallow) {
613 | if (x == u.ux && y == u.uy) display_self();
614 | return;
615 | }
616 | if (Underwater && !Is_waterlevel(&u.uz)) {
617 | /* don't do anything unless (x,y) is an adjacent underwater position */
618 | int dx, dy;
619 | if (!is_pool(x,y)) return;
620 | dx = x - u.ux; if (dx < 0) dx = -dx;
621 | dy = y - u.uy; if (dy < 0) dy = -dy;
622 | if (dx > 1 || dy > 1) return;
623 | }
624 |
625 | /* Can physically see the location. */
626 | if (cansee(x,y)) {
627 | NhRegion* reg = visible_region_at(x,y);
628 | /*
629 | * Don't use templit here: E.g.
630 | *
631 | * lev->waslit = !!(lev->lit || templit(x,y));
632 | *
633 | * Otherwise we have the "light pool" problem, where non-permanently
634 | * lit areas just out of sight stay remembered as lit. They should
635 | * re-darken.
636 | *
637 | * Perhaps ALL areas should revert to their "unlit" look when
638 | * out of sight.
639 | */
640 | lev->waslit = (lev->lit!=0); /* remember lit condition */
641 |
642 | if (reg != NULL && ACCESSIBLE(lev->typ)) {
643 | show_region(reg,x,y);
644 | return;
645 | }
646 | if (x == u.ux && y == u.uy) {
647 | if (canseeself()) {
648 | _map_location(x,y,0); /* map *under* self */
649 | display_self();
650 | } else
651 | /* we can see what is there */
652 | _map_location(x,y,1);
653 | }
654 | else {
655 | mon = m_at(x,y);
656 | worm_tail = mon && ((x != mon->mx) || (y != mon->my));
657 | if (mon &&
658 | ((see_it = (worm_tail
659 | ? (!mon->minvis || See_invisible)
660 | : (mon_visible(mon)) || sensemon(mon))))) {
661 | _map_location(x,y,0); /* map under the monster */
662 | /* also gets rid of any invisibility glyph */
663 | display_monster(x,y,mon,see_it,worm_tail);
664 | }
665 | else if (glyph_is_invisible(levl[x][y].glyph))
666 | map_invisible(x, y);
667 | else
668 | _map_location(x,y,1); /* map the location */
669 | }
670 | }
671 |
672 | /* Can't see the location. */
673 | else {
674 | if (x == u.ux && y == u.uy) {
675 | feel_location(u.ux, u.uy); /* forces an update */
676 |
677 | if (canseeself()) display_self();
678 | }
679 | else if ((mon = m_at(x,y))
680 | && (sensemon(mon)
681 | || (see_with_infrared(mon) && mon_visible(mon)))
682 | && !((x != mon->mx) || (y != mon->my))) {
683 | /* Monsters are printed every time. */
684 | /* This also gets rid of any invisibility glyph */
685 | display_monster(x,y,mon,0,0);
686 | }
687 | else if ((mon = m_at(x,y)) && mon_warning(mon) &&
688 | !((x != mon->mx) || (y != mon->my))) {
689 | display_warning(mon);
690 | }
691 |
692 | /*
693 | * If the location is remembered as being both dark (waslit is false)
694 | * and lit (glyph is a lit room or lit corridor) then it was either:
695 | *
696 | * (1) A dark location that the hero could see through night
697 | * vision.
698 | *
699 | * (2) Darkened while out of the hero's sight. This can happen
700 | * when cursed scroll of light is read.
701 | *
702 | * In either case, we have to manually correct the hero's memory to
703 | * match waslit. Deciding when to change waslit is non-trivial.
704 | *
705 | * Note: If flags.lit_corridor is set, then corridors act like room
706 | * squares. That is, they light up if in night vision range.
707 | * If flags.lit_corridor is not set, then corridors will
708 | * remain dark unless lit by a light spell.
709 | *
710 | * These checks and changes must be here and not in back_to_glyph().
711 | * They are dependent on the position being out of sight.
712 | */
713 | else if (!lev->waslit) {
714 | if (flags.lit_corridor && lev->glyph == cmap_to_glyph(S_litcorr) &&
715 | lev->typ == CORR)
716 | show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr));
717 | else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM)
718 | show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone));
719 | else
720 | goto show_mem;
721 | } else {
722 | show_mem:
723 | show_glyph(x, y, lev->glyph);
724 | }
725 | }
726 | }
727 |
728 |
729 | /*
730 | * shieldeff()
731 | *
732 | * Put magic shield pyrotechnics at the given location. This *could* be
733 | * pulled into a platform dependent routine for fancier graphics if desired.
734 | */
735 | void
736 | shieldeff(x,y)
737 | xchar x,y;
738 | {
739 | register int i;
740 |
741 | if (cansee(x,y)) { /* Don't see anything if can't see the location */
742 | for (i = 0; i < SHIELD_COUNT; i++) {
743 | show_glyph(x, y, cmap_to_glyph(shield_static[i]));
744 | flush_screen(1); /* make sure the glyph shows up */
745 | delay_output();
746 | }
747 | newsym(x,y); /* restore the old information */
748 | }
749 | }
750 |
751 |
752 | /*
753 | * tmp_at()
754 | *
755 | * Temporarily place glyphs on the screen. Do not call delay_output(). It
756 | * is up to the caller to decide if it wants to wait [presently, everyone
757 | * but explode() wants to delay].
758 | *
759 | * Call:
760 | * (DISP_BEAM, glyph) open, initialize glyph
761 | * (DISP_FLASH, glyph) open, initialize glyph
762 | * (DISP_ALWAYS, glyph) open, initialize glyph
763 | * (DISP_CHANGE, glyph) change glyph
764 | * (DISP_END, 0) close & clean up (second argument doesn't
765 | * matter)
766 | * (DISP_FREEMEM, 0) only used to prevent memory leak during
767 | * exit)
768 | * (x, y) display the glyph at the location
769 | *
770 | * DISP_BEAM - Display the given glyph at each location, but do not erase
771 | * any until the close call.
772 | * DISP_FLASH - Display the given glyph at each location, but erase the
773 | * previous location's glyph.
774 | * DISP_ALWAYS- Like DISP_FLASH, but vision is not taken into account.
775 | */
776 |
777 | static struct tmp_glyph {
778 | coord saved[COLNO]; /* previously updated positions */
779 | int sidx; /* index of next unused slot in saved[] */
780 | int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */
781 | int glyph; /* glyph to use when printing */
782 | struct tmp_glyph *prev;
783 | } tgfirst;
784 |
785 | void
786 | tmp_at(x, y)
787 | int x, y;
788 | {
789 | static struct tmp_glyph *tglyph = (struct tmp_glyph *)0;
790 | struct tmp_glyph *tmp;
791 |
792 | switch (x) {
793 | case DISP_BEAM:
794 | case DISP_FLASH:
795 | case DISP_ALWAYS:
796 | if (!tglyph)
797 | tmp = &tgfirst;
798 | else /* nested effect; we need dynamic memory */
799 | tmp = (struct tmp_glyph *)alloc(sizeof (struct tmp_glyph));
800 | tmp->prev = tglyph;
801 | tglyph = tmp;
802 | tglyph->sidx = 0;
803 | tglyph->style = x;
804 | tglyph->glyph = y;
805 | flush_screen(0); /* flush buffered glyphs */
806 | return;
807 |
808 | case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */
809 | while (tglyph) {
810 | tmp = tglyph->prev;
811 | if (tglyph != &tgfirst) free((genericptr_t)tglyph);
812 | tglyph = tmp;
813 | }
814 | return;
815 |
816 | default:
817 | break;
818 | }
819 |
820 | if (!tglyph) panic("tmp_at: tglyph not initialized");
821 |
822 | switch (x) {
823 | case DISP_CHANGE:
824 | tglyph->glyph = y;
825 | break;
826 |
827 | case DISP_END:
828 | if (tglyph->style == DISP_BEAM) {
829 | register int i;
830 |
831 | /* Erase (reset) from source to end */
832 | for (i = 0; i < tglyph->sidx; i++)
833 | newsym(tglyph->saved[i].x, tglyph->saved[i].y);
834 | } else { /* DISP_FLASH or DISP_ALWAYS */
835 | if (tglyph->sidx) /* been called at least once */
836 | newsym(tglyph->saved[0].x, tglyph->saved[0].y);
837 | }
838 | /* tglyph->sidx = 0; -- about to be freed, so not necessary */
839 | tmp = tglyph->prev;
840 | if (tglyph != &tgfirst) free((genericptr_t)tglyph);
841 | tglyph = tmp;
842 | break;
843 |
844 | default: /* do it */
845 | if (tglyph->style == DISP_BEAM) {
846 | if (!cansee(x,y)) break;
847 | /* save pos for later erasing */
848 | tglyph->saved[tglyph->sidx].x = x;
849 | tglyph->saved[tglyph->sidx].y = y;
850 | tglyph->sidx += 1;
851 | } else { /* DISP_FLASH/ALWAYS */
852 | if (tglyph->sidx) { /* not first call, so reset previous pos */
853 | newsym(tglyph->saved[0].x, tglyph->saved[0].y);
854 | tglyph->sidx = 0; /* display is presently up to date */
855 | }
856 | if (!cansee(x,y) && tglyph->style != DISP_ALWAYS) break;
857 | tglyph->saved[0].x = x;
858 | tglyph->saved[0].y = y;
859 | tglyph->sidx = 1;
860 | }
861 |
862 | show_glyph(x, y, tglyph->glyph); /* show it */
863 | flush_screen(0); /* make sure it shows up */
864 | break;
865 | } /* end case */
866 | }
867 |
868 |
869 | /*
870 | * swallowed()
871 | *
872 | * The hero is swallowed. Show a special graphics sequence for this. This
873 | * bypasses all of the display routines and messes with buffered screen
874 | * directly. This method works because both vision and display check for
875 | * being swallowed.
876 | */
877 | void
878 | swallowed(first)
879 | int first;
880 | {
881 | static xchar lastx, lasty; /* last swallowed position */
882 | int swallower, left_ok, rght_ok;
883 |
884 | if (first)
885 | cls();
886 | else {
887 | register int x, y;
888 |
889 | /* Clear old location */
890 | for (y = lasty-1; y <= lasty+1; y++)
891 | for (x = lastx-1; x <= lastx+1; x++)
892 | if (isok(x,y)) show_glyph(x,y,cmap_to_glyph(S_stone));
893 | }
894 |
895 | swallower = monsndx(u.ustuck->data);
896 | /* assume isok(u.ux,u.uy) */
897 | left_ok = isok(u.ux-1,u.uy);
898 | rght_ok = isok(u.ux+1,u.uy);
899 | /*
900 | * Display the hero surrounded by the monster's stomach.
901 | */
902 | if(isok(u.ux, u.uy-1)) {
903 | if (left_ok)
904 | show_glyph(u.ux-1, u.uy-1, swallow_to_glyph(swallower, S_sw_tl));
905 | show_glyph(u.ux , u.uy-1, swallow_to_glyph(swallower, S_sw_tc));
906 | if (rght_ok)
907 | show_glyph(u.ux+1, u.uy-1, swallow_to_glyph(swallower, S_sw_tr));
908 | }
909 |
910 | if (left_ok)
911 | show_glyph(u.ux-1, u.uy , swallow_to_glyph(swallower, S_sw_ml));
912 | display_self();
913 | if (rght_ok)
914 | show_glyph(u.ux+1, u.uy , swallow_to_glyph(swallower, S_sw_mr));
915 |
916 | if(isok(u.ux, u.uy+1)) {
917 | if (left_ok)
918 | show_glyph(u.ux-1, u.uy+1, swallow_to_glyph(swallower, S_sw_bl));
919 | show_glyph(u.ux , u.uy+1, swallow_to_glyph(swallower, S_sw_bc));
920 | if (rght_ok)
921 | show_glyph(u.ux+1, u.uy+1, swallow_to_glyph(swallower, S_sw_br));
922 | }
923 |
924 | /* Update the swallowed position. */
925 | lastx = u.ux;
926 | lasty = u.uy;
927 | }
928 |
929 | /*
930 | * under_water()
931 | *
932 | * Similar to swallowed() in operation. Shows hero when underwater
933 | * except when in water level. Special routines exist for that.
934 | */
935 | void
936 | under_water(mode)
937 | int mode;
938 | {
939 | static xchar lastx, lasty;
940 | static boolean dela;
941 | register int x, y;
942 |
943 | /* swallowing has a higher precedence than under water */
944 | if (Is_waterlevel(&u.uz) || u.uswallow) return;
945 |
946 | /* full update */
947 | if (mode == 1 || dela) {
948 | cls();
949 | dela = FALSE;
950 | }
951 | /* delayed full update */
952 | else if (mode == 2) {
953 | dela = TRUE;
954 | return;
955 | }
956 | /* limited update */
957 | else {
958 | for (y = lasty-1; y <= lasty+1; y++)
959 | for (x = lastx-1; x <= lastx+1; x++)
960 | if (isok(x,y))
961 | show_glyph(x,y,cmap_to_glyph(S_stone));
962 | }
963 | for (x = u.ux-1; x <= u.ux+1; x++)
964 | for (y = u.uy-1; y <= u.uy+1; y++)
965 | if (isok(x,y) && is_pool(x,y)) {
966 | if (Blind && !(x == u.ux && y == u.uy))
967 | show_glyph(x,y,cmap_to_glyph(S_stone));
968 | else
969 | newsym(x,y);
970 | }
971 | lastx = u.ux;
972 | lasty = u.uy;
973 | }
974 |
975 | /*
976 | * under_ground()
977 | *
978 | * Very restricted display. You can only see yourself.
979 | */
980 | void
981 | under_ground(mode)
982 | int mode;
983 | {
984 | static boolean dela;
985 |
986 | /* swallowing has a higher precedence than under ground */
987 | if (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 | newsym(u.ux,u.uy);
1002 | }
1003 |
1004 |
1005 | /* ========================================================================= */
1006 |
1007 | /*
1008 | * Loop through all of the monsters and update them. Called when:
1009 | * + going blind & telepathic
1010 | * + regaining sight & telepathic
1011 | * + getting and losing infravision
1012 | * + hallucinating
1013 | * + doing a full screen redraw
1014 | * + see invisible times out or a ring of see invisible is taken off
1015 | * + when a potion of see invisible is quaffed or a ring of see
1016 | * invisible is put on
1017 | * + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c]
1018 | * + losing telepathy while blind [xkilled() in mon.c, attrcurse() in
1019 | * sit.c]
1020 | */
1021 | void
1022 | see_monsters()
1023 | {
1024 | register struct monst *mon;
1025 | for (mon = fmon; mon; mon = mon->nmon) {
1026 | if (DEADMONSTER(mon)) continue;
1027 | newsym(mon->mx,mon->my);
1028 | if (mon->wormno) see_wsegs(mon);
1029 | }
1030 | }
1031 |
1032 | /*
1033 | * Block/unblock light depending on what a mimic is mimicing and if it's
1034 | * invisible or not. Should be called only when the state of See_invisible
1035 | * changes.
1036 | */
1037 | void
1038 | set_mimic_blocking()
1039 | {
1040 | register struct monst *mon;
1041 | for (mon = fmon; mon; mon = mon->nmon)
1042 | if(!DEADMONSTER(mon) && mon->minvis &&
1043 | ((mon->m_ap_type == M_AP_FURNITURE &&
1044 | (mon->mappearance == S_vcdoor || mon->mappearance == S_hcdoor))||
1045 | (mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER))) {
1046 | if(See_invisible)
1047 | block_point(mon->mx, mon->my);
1048 | else
1049 | unblock_point(mon->mx, mon->my);
1050 | }
1051 | }
1052 |
1053 | /*
1054 | * Loop through all of the object *locations* and update them. Called when
1055 | * + hallucinating.
1056 | */
1057 | void
1058 | see_objects()
1059 | {
1060 | register struct obj *obj;
1061 | for(obj = fobj; obj; obj = obj->nobj)
1062 | if (vobj_at(obj->ox,obj->oy) == obj) newsym(obj->ox, obj->oy);
1063 | }
1064 |
1065 | /*
1066 | * Update hallucinated traps.
1067 | */
1068 | void
1069 | see_traps()
1070 | {
1071 | struct trap *trap;
1072 | int glyph;
1073 |
1074 | for (trap = ftrap; trap; trap = trap->ntrap) {
1075 | glyph = glyph_at(trap->tx, trap->ty);
1076 | if (glyph_is_trap(glyph))
1077 | newsym(trap->tx, trap->ty);
1078 | }
1079 | }
1080 |
1081 | /*
1082 | * Put the cursor on the hero. Flush all accumulated glyphs before doing it.
1083 | */
1084 | void
1085 | curs_on_u()
1086 | {
1087 | flush_screen(1); /* Flush waiting glyphs & put cursor on hero */
1088 | }
1089 |
1090 | int
1091 | doredraw()
1092 | {
1093 | docrt();
1094 | return 0;
1095 | }
1096 |
1097 | void
1098 | docrt()
1099 | {
1100 | register int x,y;
1101 | register struct rm *lev;
1102 |
1103 | if (!u.ux) return; /* display isn't ready yet */
1104 |
1105 | if (u.uswallow) {
1106 | swallowed(1);
1107 | return;
1108 | }
1109 | if (Underwater && !Is_waterlevel(&u.uz)) {
1110 | under_water(1);
1111 | return;
1112 | }
1113 | if (u.uburied) {
1114 | under_ground(1);
1115 | return;
1116 | }
1117 |
1118 | /* shut down vision */
1119 | vision_recalc(2);
1120 |
1121 | /*
1122 | * This routine assumes that cls() does the following:
1123 | * + fills the physical screen with the symbol for rock
1124 | * + clears the glyph buffer
1125 | */
1126 | cls();
1127 |
1128 | /* display memory */
1129 | for (x = 1; x < COLNO; x++) {
1130 | lev = &levl[x][0];
1131 | for (y = 0; y < ROWNO; y++, lev++)
1132 | if (lev->glyph != cmap_to_glyph(S_stone))
1133 | show_glyph(x,y,lev->glyph);
1134 | }
1135 |
1136 | /* see what is to be seen */
1137 | vision_recalc(0);
1138 |
1139 | /* overlay with monsters */
1140 | see_monsters();
1141 |
1142 | flags.botlx = 1; /* force a redraw of the bottom line */
1143 | }
1144 |
1145 |
1146 | /* ========================================================================= */
1147 | /* Glyph Buffering (3rd screen) ============================================ */
1148 |
1149 | typedef struct {
1150 | xchar new; /* perhaps move this bit into the rm strucure. */
1151 | int glyph;
1152 | } gbuf_entry;
1153 |
1154 | static gbuf_entry gbuf[ROWNO][COLNO];
1155 | static char gbuf_start[ROWNO];
1156 | static char gbuf_stop[ROWNO];
1157 |
1158 | /*
1159 | * Store the glyph in the 3rd screen for later flushing.
1160 | */
1161 | void
1162 | show_glyph(x,y,glyph)
1163 | int x, y, glyph;
1164 | {
1165 | /*
1166 | * Check for bad positions and glyphs.
1167 | */
1168 | if (!isok(x, y)) {
1169 | const char *text;
1170 | int offset;
1171 |
1172 | /* column 0 is invalid, but it's often used as a flag, so ignore it */
1173 | if (x == 0) return;
1174 |
1175 | /*
1176 | * This assumes an ordering of the offsets. See display.h for
1177 | * the definition.
1178 | */
1179 |
1180 | if (glyph >= GLYPH_WARNING_OFF) { /* a warning */
1181 | text = "warning"; offset = glyph - GLYPH_WARNING_OFF;
1182 | } else if (glyph >= GLYPH_SWALLOW_OFF) { /* swallow border */
1183 | text = "swallow border"; offset = glyph - GLYPH_SWALLOW_OFF;
1184 | } else if (glyph >= GLYPH_ZAP_OFF) { /* zap beam */
1185 | text = "zap beam"; offset = glyph - GLYPH_ZAP_OFF;
1186 | } else if (glyph >= GLYPH_CMAP_OFF) { /* cmap */
1187 | text = "cmap_index"; offset = glyph - GLYPH_CMAP_OFF;
1188 | } else if (glyph >= GLYPH_OBJ_OFF) { /* object */
1189 | text = "object"; offset = glyph - GLYPH_OBJ_OFF;
1190 | } else if (glyph >= GLYPH_RIDDEN_OFF) { /* ridden mon */
1191 | text = "ridden mon"; offset = glyph - GLYPH_RIDDEN_OFF;
1192 | } else if (glyph >= GLYPH_BODY_OFF) { /* a corpse */
1193 | text = "corpse"; offset = glyph - GLYPH_BODY_OFF;
1194 | } else if (glyph >= GLYPH_DETECT_OFF) { /* detected mon */
1195 | text = "detected mon"; offset = glyph - GLYPH_DETECT_OFF;
1196 | } else if (glyph >= GLYPH_INVIS_OFF) { /* invisible mon */
1197 | text = "invisible mon"; offset = glyph - GLYPH_INVIS_OFF;
1198 | } else if (glyph >= GLYPH_PET_OFF) { /* a pet */
1199 | text = "pet"; offset = glyph - GLYPH_PET_OFF;
1200 | } else { /* a monster */
1201 | text = "monster"; offset = glyph;
1202 | }
1203 |
1204 | impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].",
1205 | x, y, glyph, text, offset);
1206 | return;
1207 | }
1208 |
1209 | if (glyph >= MAX_GLYPH) {
1210 | impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).",
1211 | glyph, MAX_GLYPH, x, y);
1212 | return;
1213 | }
1214 |
1215 | if (gbuf[y][x].glyph != glyph) {
1216 | gbuf[y][x].glyph = glyph;
1217 | gbuf[y][x].new = 1;
1218 | if (gbuf_start[y] > x) gbuf_start[y] = x;
1219 | if (gbuf_stop[y] < x) gbuf_stop[y] = x;
1220 | }
1221 | }
1222 |
1223 |
1224 | /*
1225 | * Reset the changed glyph borders so that none of the 3rd screen has
1226 | * changed.
1227 | */
1228 | #define reset_glyph_bbox() \
1229 | { \
1230 | int i; \
1231 | \
1232 | for (i = 0; i < ROWNO; i++) { \
1233 | gbuf_start[i] = COLNO-1; \
1234 | gbuf_stop[i] = 0; \
1235 | } \
1236 | }
1237 |
1238 |
1239 | static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) };
1240 | /*
1241 | * Turn the 3rd screen into stone.
1242 | */
1243 | void
1244 | clear_glyph_buffer()
1245 | {
1246 | register int x, y;
1247 | register gbuf_entry *gptr;
1248 |
1249 | for (y = 0; y < ROWNO; y++) {
1250 | gptr = &gbuf[y][0];
1251 | for (x = COLNO; x; x--) {
1252 | *gptr++ = nul_gbuf;
1253 | }
1254 | }
1255 | reset_glyph_bbox();
1256 | }
1257 |
1258 | /*
1259 | * Assumes that the indicated positions are filled with S_stone glyphs.
1260 | */
1261 | void
1262 | row_refresh(start,stop,y)
1263 | int start,stop,y;
1264 | {
1265 | register int x;
1266 |
1267 | for (x = start; x <= stop; x++)
1268 | if (gbuf[y][x].glyph != cmap_to_glyph(S_stone))
1269 | print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph);
1270 | }
1271 |
1272 | void
1273 | cls()
1274 | {
1275 | display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1276 | flags.botlx = 1; /* force update of botl window */
1277 | clear_nhwindow(WIN_MAP); /* clear physical screen */
1278 |
1279 | clear_glyph_buffer(); /* this is sort of an extra effort, but OK */
1280 | }
1281 |
1282 | /*
1283 | * Synch the third screen with the display.
1284 | */
1285 | void
1286 | flush_screen(cursor_on_u)
1287 | int cursor_on_u;
1288 | {
1289 | /* Prevent infinite loops on errors:
1290 | * flush_screen->print_glyph->impossible->pline->flush_screen
1291 | */
1292 | static boolean flushing = 0;
1293 | register int x,y;
1294 |
1295 | if (flushing) return; /* if already flushing then return */
1296 | flushing = 1;
1297 |
1298 | for (y = 0; y < ROWNO; y++) {
1299 | register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]];
1300 | for (; x <= gbuf_stop[y]; gptr++, x++)
1301 | if (gptr->new) {
1302 | print_glyph(WIN_MAP,x,y,gptr->glyph);
1303 | gptr->new = 0;
1304 | }
1305 | }
1306 |
1307 | if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */
1308 | display_nhwindow(WIN_MAP, FALSE);
1309 | reset_glyph_bbox();
1310 | flushing = 0;
1311 | if(flags.botl || flags.botlx) bot();
1312 | }
1313 |
1314 | /* ========================================================================= */
1315 |
1316 | /*
1317 | * back_to_glyph()
1318 | *
1319 | * Use the information in the rm structure at the given position to create
1320 | * a glyph of a background.
1321 | *
1322 | * I had to add a field in the rm structure (horizontal) so that we knew
1323 | * if open doors and secret doors were horizontal or vertical. Previously,
1324 | * the screen symbol had the horizontal/vertical information set at
1325 | * level generation time.
1326 | *
1327 | * I used the 'ladder' field (really doormask) for deciding if stairwells
1328 | * were up or down. I didn't want to check the upstairs and dnstairs
1329 | * variables.
1330 | */
1331 | int
1332 | back_to_glyph(x,y)
1333 | xchar x,y;
1334 | {
1335 | int idx;
1336 | struct rm *ptr = &(levl[x][y]);
1337 |
1338 | switch (ptr->typ) {
1339 | case SCORR:
1340 | case STONE:
1341 | idx = level.flags.arboreal ? S_tree : S_stone;
1342 | break;
1343 | case ROOM: idx = S_room; break;
1344 | case CORR:
1345 | idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr;
1346 | break;
1347 | case HWALL:
1348 | case VWALL:
1349 | case TLCORNER:
1350 | case TRCORNER:
1351 | case BLCORNER:
1352 | case BRCORNER:
1353 | case CROSSWALL:
1354 | case TUWALL:
1355 | case TDWALL:
1356 | case TLWALL:
1357 | case TRWALL:
1358 | case SDOOR:
1359 | idx = ptr->seenv ? wall_angle(ptr) : S_stone;
1360 | break;
1361 | case DOOR:
1362 | if (ptr->doormask) {
1363 | if (ptr->doormask & D_BROKEN)
1364 | idx = S_ndoor;
1365 | else if (ptr->doormask & D_ISOPEN)
1366 | idx = (ptr->horizontal) ? S_hodoor : S_vodoor;
1367 | else /* else is closed */
1368 | idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor;
1369 | } else
1370 | idx = S_ndoor;
1371 | break;
1372 | case IRONBARS: idx = S_bars; break;
1373 | case TREE: idx = S_tree; break;
1374 | case POOL:
1375 | case MOAT: idx = S_pool; break;
1376 | case STAIRS:
1377 | idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair;
1378 | break;
1379 | case LADDER:
1380 | idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder;
1381 | break;
1382 | case FOUNTAIN: idx = S_fountain; break;
1383 | case SINK: idx = S_sink; break;
1384 | case ALTAR: idx = S_altar; break;
1385 | case GRAVE: idx = S_grave; break;
1386 | case THRONE: idx = S_throne; break;
1387 | case LAVAPOOL: idx = S_lava; break;
1388 | case ICE: idx = S_ice; break;
1389 | case AIR: idx = S_air; break;
1390 | case CLOUD: idx = S_cloud; break;
1391 | case WATER: idx = S_water; break;
1392 | case DBWALL:
1393 | idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge;
1394 | break;
1395 | case DRAWBRIDGE_UP:
1396 | switch(ptr->drawbridgemask & DB_UNDER) {
1397 | case DB_MOAT: idx = S_pool; break;
1398 | case DB_LAVA: idx = S_lava; break;
1399 | case DB_ICE: idx = S_ice; break;
1400 | case DB_FLOOR: idx = S_room; break;
1401 | default:
1402 | impossible("Strange db-under: %d",
1403 | ptr->drawbridgemask & DB_UNDER);
1404 | idx = S_room; /* something is better than nothing */
1405 | break;
1406 | }
1407 | break;
1408 | case DRAWBRIDGE_DOWN:
1409 | idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge;
1410 | break;
1411 | default:
1412 | impossible("back_to_glyph: unknown level type [ = %d ]",ptr->typ);
1413 | idx = S_room;
1414 | break;
1415 | }
1416 |
1417 | return cmap_to_glyph(idx);
1418 | }
1419 |
1420 |
1421 | /*
1422 | * swallow_to_glyph()
1423 | *
1424 | * Convert a monster number and a swallow location into the correct glyph.
1425 | * If you don't want a patchwork monster while hallucinating, decide on
1426 | * a random monster in swallowed() and don't use what_mon() here.
1427 | */
1428 | STATIC_OVL int
1429 | swallow_to_glyph(mnum, loc)
1430 | int mnum;
1431 | int loc;
1432 | {
1433 | if (loc < S_sw_tl || S_sw_br < loc) {
1434 | impossible("swallow_to_glyph: bad swallow location");
1435 | loc = S_sw_br;
1436 | }
1437 | return ((int) (what_mon(mnum)<<3) | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF;
1438 | }
1439 |
1440 |
1441 |
1442 | /*
1443 | * zapdir_to_glyph()
1444 | *
1445 | * Change the given zap direction and beam type into a glyph. Each beam
1446 | * type has four glyphs, one for each of the symbols below. The order of
1447 | * the zap symbols [0-3] as defined in rm.h are:
1448 | *
1449 | * | S_vbeam ( 0, 1) or ( 0,-1)
1450 | * - S_hbeam ( 1, 0) or (-1, 0)
1451 | * \ S_lslant ( 1, 1) or (-1,-1)
1452 | * / S_rslant (-1, 1) or ( 1,-1)
1453 | */
1454 | int
1455 | zapdir_to_glyph(dx, dy, beam_type)
1456 | register int dx, dy;
1457 | int beam_type;
1458 | {
1459 | if (beam_type >= NUM_ZAP) {
1460 | impossible("zapdir_to_glyph: illegal beam type");
1461 | beam_type = 0;
1462 | }
1463 | dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0;
1464 |
1465 | return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF;
1466 | }
1467 |
1468 |
1469 | /*
1470 | * Utility routine for dowhatis() used to find out the glyph displayed at
1471 | * the location. This isn't necessarily the same as the glyph in the levl
1472 | * structure, so we must check the "third screen".
1473 | */
1474 | int
1475 | glyph_at(x, y)
1476 | xchar x,y;
1477 | {
1478 | if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO)
1479 | return cmap_to_glyph(S_room); /* XXX */
1480 | return gbuf[y][x].glyph;
1481 | }
1482 |
1483 |
1484 | /* ------------------------------------------------------------------------- */
1485 | /* Wall Angle -------------------------------------------------------------- */
1486 |
1487 | /*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */
1488 |
1489 | #ifdef WA_VERBOSE
1490 |
1491 | static const char *FDECL(type_to_name, (int));
1492 | static void FDECL(error4, (int,int,int,int,int,int));
1493 |
1494 | static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */
1495 | static const char *type_names[MAX_TYPE] = {
1496 | "STONE", "VWALL", "HWALL", "TLCORNER",
1497 | "TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL",
1498 | "TUWALL", "TDWALL", "TLWALL", "TRWALL",
1499 | "DBWALL", "SDOOR", "SCORR", "POOL",
1500 | "MOAT", "WATER", "DRAWBRIDGE_UP","LAVAPOOL",
1501 | "DOOR", "CORR", "ROOM", "STAIRS",
1502 | "LADDER", "FOUNTAIN", "THRONE", "SINK",
1503 | "ALTAR", "ICE", "DRAWBRIDGE_DOWN","AIR",
1504 | "CLOUD"
1505 | };
1506 |
1507 |
1508 | static const char *
1509 | type_to_name(type)
1510 | int type;
1511 | {
1512 | return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type];
1513 | }
1514 |
1515 | static void
1516 | error4(x, y, a, b, c, dd)
1517 | int x, y, a, b, c, dd;
1518 | {
1519 | pline("set_wall_state: %s @ (%d,%d) %s%s%s%s",
1520 | type_to_name(levl[x][y].typ), x, y,
1521 | a ? "1":"", b ? "2":"", c ? "3":"", dd ? "4":"");
1522 | bad_count[levl[x][y].typ]++;
1523 | }
1524 | #endif /* WA_VERBOSE */
1525 |
1526 | /*
1527 | * Return 'which' if position is implies an unfinshed exterior. Return
1528 | * zero otherwise. Unfinished implies outer area is rock or a corridor.
1529 | *
1530 | * Things that are ambigious: lava
1531 | */
1532 | STATIC_OVL int
1533 | check_pos(x, y, which)
1534 | int x, y, which;
1535 | {
1536 | int type;
1537 | if (!isok(x,y)) return which;
1538 | type = levl[x][y].typ;
1539 | if (IS_ROCK(type) || type == CORR || type == SCORR) return which;
1540 | return 0;
1541 | }
1542 |
1543 | /* Return TRUE if more than one is non-zero. */
1544 | /*ARGSUSED*/
1545 | #ifdef WA_VERBOSE
1546 | STATIC_OVL boolean
1547 | more_than_one(x, y, a, b, c)
1548 | int x, y, a, b, c;
1549 | {
1550 | if ((a && (b|c)) || (b && (a|c)) || (c && (a|b))) {
1551 | error4(x,y,a,b,c,0);
1552 | return TRUE;
1553 | }
1554 | return FALSE;
1555 | }
1556 | #else
1557 | #define more_than_one(x, y, a, b, c) (((a) && ((b)|(c))) || ((b) && ((a)|(c))) || ((c) && ((a)|(b))))
1558 | #endif
1559 |
1560 | /* Return the wall mode for a T wall. */
1561 | STATIC_OVL int
1562 | set_twall(x0,y0, x1,y1, x2,y2, x3,y3)
1563 | int x0,y0, x1,y1, x2,y2, x3,y3;
1564 | {
1565 | int wmode, is_1, is_2, is_3;
1566 |
1567 | is_1 = check_pos(x1, y1, WM_T_LONG);
1568 | is_2 = check_pos(x2, y2, WM_T_BL);
1569 | is_3 = check_pos(x3, y3, WM_T_BR);
1570 | if (more_than_one(x0, y0, is_1, is_2, is_3)) {
1571 | wmode = 0;
1572 | } else {
1573 | wmode = is_1 + is_2 + is_3;
1574 | }
1575 | return wmode;
1576 | }
1577 |
1578 | /* Return wall mode for a horizontal or vertical wall. */
1579 | STATIC_OVL int
1580 | set_wall(x, y, horiz)
1581 | int x, y, horiz;
1582 | {
1583 | int wmode, is_1, is_2;
1584 |
1585 | if (horiz) {
1586 | is_1 = check_pos(x,y-1, WM_W_TOP);
1587 | is_2 = check_pos(x,y+1, WM_W_BOTTOM);
1588 | } else {
1589 | is_1 = check_pos(x-1,y, WM_W_LEFT);
1590 | is_2 = check_pos(x+1,y, WM_W_RIGHT);
1591 | }
1592 | if (more_than_one(x, y, is_1, is_2, 0)) {
1593 | wmode = 0;
1594 | } else {
1595 | wmode = is_1 + is_2;
1596 | }
1597 | return wmode;
1598 | }
1599 |
1600 |
1601 | /* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */
1602 | STATIC_OVL int
1603 | set_corn(x1,y1, x2,y2, x3,y3, x4,y4)
1604 | int x1, y1, x2, y2, x3, y3, x4, y4;
1605 | {
1606 | int wmode, is_1, is_2, is_3, is_4;
1607 |
1608 | is_1 = check_pos(x1, y1, 1);
1609 | is_2 = check_pos(x2, y2, 1);
1610 | is_3 = check_pos(x3, y3, 1);
1611 | is_4 = check_pos(x4, y4, 1); /* inner location */
1612 |
1613 | /*
1614 | * All 4 should not be true. So if the inner location is rock,
1615 | * use it. If all of the outer 3 are true, use outer. We currently
1616 | * can't cover the case where only part of the outer is rock, so
1617 | * we just say that all the walls are finished (if not overridden
1618 | * by the inner section).
1619 | */
1620 | if (is_4) {
1621 | wmode = WM_C_INNER;
1622 | } else if (is_1 && is_2 && is_3)
1623 | wmode = WM_C_OUTER;
1624 | else
1625 | wmode = 0; /* finished walls on all sides */
1626 |
1627 | return wmode;
1628 | }
1629 |
1630 | /* Return mode for a crosswall. */
1631 | STATIC_OVL int
1632 | set_crosswall(x, y)
1633 | int x, y;
1634 | {
1635 | int wmode, is_1, is_2, is_3, is_4;
1636 |
1637 | is_1 = check_pos(x-1, y-1, 1);
1638 | is_2 = check_pos(x+1, y-1, 1);
1639 | is_3 = check_pos(x+1, y+1, 1);
1640 | is_4 = check_pos(x-1, y+1, 1);
1641 |
1642 | wmode = is_1+is_2+is_3+is_4;
1643 | if (wmode > 1) {
1644 | if (is_1 && is_3 && (is_2+is_4 == 0)) {
1645 | wmode = WM_X_TLBR;
1646 | } else if (is_2 && is_4 && (is_1+is_3 == 0)) {
1647 | wmode = WM_X_BLTR;
1648 | } else {
1649 | #ifdef WA_VERBOSE
1650 | error4(x,y,is_1,is_2,is_3,is_4);
1651 | #endif
1652 | wmode = 0;
1653 | }
1654 | } else if (is_1)
1655 | wmode = WM_X_TL;
1656 | else if (is_2)
1657 | wmode = WM_X_TR;
1658 | else if (is_3)
1659 | wmode = WM_X_BR;
1660 | else if (is_4)
1661 | wmode = WM_X_BL;
1662 |
1663 | return wmode;
1664 | }
1665 |
1666 | /* Called from mklev. Scan the level and set the wall modes. */
1667 | void
1668 | set_wall_state()
1669 | {
1670 | int x, y;
1671 | int wmode;
1672 | struct rm *lev;
1673 |
1674 | #ifdef WA_VERBOSE
1675 | for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0;
1676 | #endif
1677 |
1678 | for (x = 0; x < COLNO; x++)
1679 | for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) {
1680 | switch (lev->typ) {
1681 | case SDOOR:
1682 | wmode = set_wall(x, y, (int) lev->horizontal);
1683 | break;
1684 | case VWALL:
1685 | wmode = set_wall(x, y, 0);
1686 | break;
1687 | case HWALL:
1688 | wmode = set_wall(x, y, 1);
1689 | break;
1690 | case TDWALL:
1691 | wmode = set_twall(x,y, x,y-1, x-1,y+1, x+1,y+1);
1692 | break;
1693 | case TUWALL:
1694 | wmode = set_twall(x,y, x,y+1, x+1,y-1, x-1,y-1);
1695 | break;
1696 | case TLWALL:
1697 | wmode = set_twall(x,y, x+1,y, x-1,y-1, x-1,y+1);
1698 | break;
1699 | case TRWALL:
1700 | wmode = set_twall(x,y, x-1,y, x+1,y+1, x+1,y-1);
1701 | break;
1702 | case TLCORNER:
1703 | wmode = set_corn(x-1,y-1, x,y-1, x-1,y, x+1,y+1);
1704 | break;
1705 | case TRCORNER:
1706 | wmode = set_corn(x,y-1, x+1,y-1, x+1,y, x-1,y+1);
1707 | break;
1708 | case BLCORNER:
1709 | wmode = set_corn(x,y+1, x-1,y+1, x-1,y, x+1,y-1);
1710 | break;
1711 | case BRCORNER:
1712 | wmode = set_corn(x+1,y, x+1,y+1, x,y+1, x-1,y-1);
1713 | break;
1714 | case CROSSWALL:
1715 | wmode = set_crosswall(x, y);
1716 | break;
1717 |
1718 | default:
1719 | wmode = -1; /* don't set wall info */
1720 | break;
1721 | }
1722 |
1723 | if (wmode >= 0)
1724 | lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode;
1725 | }
1726 |
1727 | #ifdef WA_VERBOSE
1728 | /* check if any bad positions found */
1729 | for (x = y = 0; x < MAX_TYPE; x++)
1730 | if (bad_count[x]) {
1731 | if (y == 0) {
1732 | y = 1; /* only print once */
1733 | pline("set_wall_type: wall mode problems with: ");
1734 | }
1735 | pline("%s %d;", type_names[x], bad_count[x]);
1736 | }
1737 | #endif /* WA_VERBOSE */
1738 | }
1739 |
1740 | /* ------------------------------------------------------------------------- */
1741 | /* This matrix is used here and in vision.c. */
1742 | unsigned char seenv_matrix[3][3] = { {SV2, SV1, SV0},
1743 | {SV3, SVALL, SV7},
1744 | {SV4, SV5, SV6} };
1745 |
1746 | #define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0))
1747 |
1748 | /* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */
1749 | STATIC_OVL void
1750 | set_seenv(lev, x0, y0, x, y)
1751 | struct rm *lev;
1752 | int x0, y0, x, y; /* from, to */
1753 | {
1754 | int dx = x-x0, dy = y0-y;
1755 | lev->seenv |= seenv_matrix[sign(dy)+1][sign(dx)+1];
1756 | }
1757 |
1758 | /* ------------------------------------------------------------------------- */
1759 |
1760 | /* T wall types, one for each row in wall_matrix[][]. */
1761 | #define T_d 0
1762 | #define T_l 1
1763 | #define T_u 2
1764 | #define T_r 3
1765 |
1766 | /*
1767 | * These are the column names of wall_matrix[][]. They are the "results"
1768 | * of a tdwall pattern match. All T walls are rotated so they become
1769 | * a tdwall. Then we do a single pattern match, but return the
1770 | * correct result for the original wall by using different rows for
1771 | * each of the wall types.
1772 | */
1773 | #define T_stone 0
1774 | #define T_tlcorn 1
1775 | #define T_trcorn 2
1776 | #define T_hwall 3
1777 | #define T_tdwall 4
1778 |
1779 | static const int wall_matrix[4][5] = {
1780 | { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */
1781 | { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */
1782 | { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */
1783 | { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */
1784 | };
1785 |
1786 |
1787 | /* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][]. */
1788 | #define C_bl 0
1789 | #define C_tl 1
1790 | #define C_tr 2
1791 | #define C_br 3
1792 |
1793 | /*
1794 | * These are the column names for cross_matrix[][]. They express results
1795 | * in C_br (bottom right) terms. All crosswalls with a single solid
1796 | * quarter are rotated so the solid section is at the bottom right.
1797 | * We pattern match on that, but return the correct result depending
1798 | * on which row we'ere looking at.
1799 | */
1800 | #define C_trcorn 0
1801 | #define C_brcorn 1
1802 | #define C_blcorn 2
1803 | #define C_tlwall 3
1804 | #define C_tuwall 4
1805 | #define C_crwall 5
1806 |
1807 | static const int cross_matrix[4][6] = {
1808 | { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall },
1809 | { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall },
1810 | { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall },
1811 | { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall },
1812 | };
1813 |
1814 |
1815 | /* Print out a T wall warning and all interesting info. */
1816 | STATIC_OVL void
1817 | t_warn(lev)
1818 | struct rm *lev;
1819 | {
1820 | static const char *warn_str = "wall_angle: %s: case %d: seenv = 0x%x";
1821 | const char *wname;
1822 |
1823 | if (lev->typ == TUWALL) wname = "tuwall";
1824 | else if (lev->typ == TLWALL) wname = "tlwall";
1825 | else if (lev->typ == TRWALL) wname = "trwall";
1826 | else if (lev->typ == TDWALL) wname = "tdwall";
1827 | else wname = "unknown";
1828 | impossible(warn_str, wname, lev->wall_info & WM_MASK,
1829 | (unsigned int) lev->seenv);
1830 | }
1831 |
1832 |
1833 | /*
1834 | * Return the correct graphics character index using wall type, wall mode,
1835 | * and the seen vector. It is expected that seenv is non zero.
1836 | *
1837 | * All T-wall vectors are rotated to be TDWALL. All single crosswall
1838 | * blocks are rotated to bottom right. All double crosswall are rotated
1839 | * to W_X_BLTR. All results are converted back.
1840 | *
1841 | * The only way to understand this is to take out pen and paper and
1842 | * draw diagrams. See rm.h for more details on the wall modes and
1843 | * seen vector (SV).
1844 | */
1845 | STATIC_OVL int
1846 | wall_angle(lev)
1847 | struct rm *lev;
1848 | {
1849 | register unsigned int seenv = lev->seenv & 0xff;
1850 | const int *row;
1851 | int col, idx;
1852 |
1853 | #define only(sv, bits) (((sv) & (bits)) && ! ((sv) & ~(bits)))
1854 | switch (lev->typ) {
1855 | case TUWALL:
1856 | row = wall_matrix[T_u];
1857 | seenv = (seenv >> 4 | seenv << 4) & 0xff;/* rotate to tdwall */
1858 | goto do_twall;
1859 | case TLWALL:
1860 | row = wall_matrix[T_l];
1861 | seenv = (seenv >> 2 | seenv << 6) & 0xff;/* rotate to tdwall */
1862 | goto do_twall;
1863 | case TRWALL:
1864 | row = wall_matrix[T_r];
1865 | seenv = (seenv >> 6 | seenv << 2) & 0xff;/* rotate to tdwall */
1866 | goto do_twall;
1867 | case TDWALL:
1868 | row = wall_matrix[T_d];
1869 | do_twall:
1870 | switch (lev->wall_info & WM_MASK) {
1871 | case 0:
1872 | if (seenv == SV4) {
1873 | col = T_tlcorn;
1874 | } else if (seenv == SV6) {
1875 | col = T_trcorn;
1876 | } else if (seenv & (SV3|SV5|SV7) ||
1877 | ((seenv & SV4) && (seenv & SV6))) {
1878 | col = T_tdwall;
1879 | } else if (seenv & (SV0|SV1|SV2)) {
1880 | col = (seenv & (SV4|SV6) ? T_tdwall : T_hwall);
1881 | } else {
1882 | t_warn(lev);
1883 | col = T_stone;
1884 | }
1885 | break;
1886 | case WM_T_LONG:
1887 | if (seenv & (SV3|SV4) && !(seenv & (SV5|SV6|SV7))) {
1888 | col = T_tlcorn;
1889 | } else if (seenv&(SV6|SV7) && !(seenv&(SV3|SV4|SV5))) {
1890 | col = T_trcorn;
1891 | } else if ((seenv & SV5) ||
1892 | ((seenv & (SV3|SV4)) && (seenv & (SV6|SV7)))) {
1893 | col = T_tdwall;
1894 | } else {
1895 | /* only SV0|SV1|SV2 */
1896 | if (! only(seenv, SV0|SV1|SV2) )
1897 | t_warn(lev);
1898 | col = T_stone;
1899 | }
1900 | break;
1901 | case WM_T_BL:
1902 | #if 0 /* older method, fixed */
1903 | if (only(seenv, SV4|SV5)) {
1904 | col = T_tlcorn;
1905 | } else if ((seenv & (SV0|SV1|SV2)) &&
1906 | only(seenv, SV0|SV1|SV2|SV6|SV7)) {
1907 | col = T_hwall;
1908 | } else if (seenv & SV3 ||
1909 | ((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) {
1910 | col = T_tdwall;
1911 | } else {
1912 | if (seenv != SV6)
1913 | t_warn(lev);
1914 | col = T_stone;
1915 | }
1916 | #endif /* 0 */
1917 | if (only(seenv, SV4|SV5))
1918 | col = T_tlcorn;
1919 | else if ((seenv & (SV0|SV1|SV2|SV7)) &&
1920 | !(seenv & (SV3|SV4|SV5)))
1921 | col = T_hwall;
1922 | else if (only(seenv, SV6))
1923 | col = T_stone;
1924 | else
1925 | col = T_tdwall;
1926 | break;
1927 | case WM_T_BR:
1928 | #if 0 /* older method, fixed */
1929 | if (only(seenv, SV5|SV6)) {
1930 | col = T_trcorn;
1931 | } else if ((seenv & (SV0|SV1|SV2)) &&
1932 | only(seenv, SV0|SV1|SV2|SV3|SV4)) {
1933 | col = T_hwall;
1934 | } else if (seenv & SV7 ||
1935 | ((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) {
1936 | col = T_tdwall;
1937 | } else {
1938 | if (seenv != SV4)
1939 | t_warn(lev);
1940 | col = T_stone;
1941 | }
1942 | #endif /* 0 */
1943 | if (only(seenv, SV5|SV6))
1944 | col = T_trcorn;
1945 | else if ((seenv & (SV0|SV1|SV2|SV3)) &&
1946 | !(seenv & (SV5|SV6|SV7)))
1947 | col = T_hwall;
1948 | else if (only(seenv, SV4))
1949 | col = T_stone;
1950 | else
1951 | col = T_tdwall;
1952 |
1953 | break;
1954 | default:
1955 | impossible("wall_angle: unknown T wall mode %d",
1956 | lev->wall_info & WM_MASK);
1957 | col = T_stone;
1958 | break;
1959 | }
1960 | idx = row[col];
1961 | break;
1962 |
1963 | case SDOOR:
1964 | if (lev->horizontal) goto horiz;
1965 | /* fall through */
1966 | case VWALL:
1967 | switch (lev->wall_info & WM_MASK) {
1968 | case 0: idx = seenv ? S_vwall : S_stone; break;
1969 | case 1: idx = seenv & (SV1|SV2|SV3|SV4|SV5) ? S_vwall :
1970 | S_stone;
1971 | break;
1972 | case 2: idx = seenv & (SV0|SV1|SV5|SV6|SV7) ? S_vwall :
1973 | S_stone;
1974 | break;
1975 | default:
1976 | impossible("wall_angle: unknown vwall mode %d",
1977 | lev->wall_info & WM_MASK);
1978 | idx = S_stone;
1979 | break;
1980 | }
1981 | break;
1982 |
1983 | case HWALL:
1984 | horiz:
1985 | switch (lev->wall_info & WM_MASK) {
1986 | case 0: idx = seenv ? S_hwall : S_stone; break;
1987 | case 1: idx = seenv & (SV3|SV4|SV5|SV6|SV7) ? S_hwall :
1988 | S_stone;
1989 | break;
1990 | case 2: idx = seenv & (SV0|SV1|SV2|SV3|SV7) ? S_hwall :
1991 | S_stone;
1992 | break;
1993 | default:
1994 | impossible("wall_angle: unknown hwall mode %d",
1995 | lev->wall_info & WM_MASK);
1996 | idx = S_stone;
1997 | break;
1998 | }
1999 | break;
2000 |
2001 | #define set_corner(idx, lev, which, outer, inner, name) \
2002 | switch ((lev)->wall_info & WM_MASK) { \
2003 | case 0: idx = which; break; \
2004 | case WM_C_OUTER: idx = seenv & (outer) ? which : S_stone; break; \
2005 | case WM_C_INNER: idx = seenv & ~(inner) ? which : S_stone; break; \
2006 | default: \
2007 | impossible("wall_angle: unknown %s mode %d", name, \
2008 | (lev)->wall_info & WM_MASK); \
2009 | idx = S_stone; \
2010 | break; \
2011 | }
2012 |
2013 | case TLCORNER:
2014 | set_corner(idx, lev, S_tlcorn, (SV3|SV4|SV5), SV4, "tlcorn");
2015 | break;
2016 | case TRCORNER:
2017 | set_corner(idx, lev, S_trcorn, (SV5|SV6|SV7), SV6, "trcorn");
2018 | break;
2019 | case BLCORNER:
2020 | set_corner(idx, lev, S_blcorn, (SV1|SV2|SV3), SV2, "blcorn");
2021 | break;
2022 | case BRCORNER:
2023 | set_corner(idx, lev, S_brcorn, (SV7|SV0|SV1), SV0, "brcorn");
2024 | break;
2025 |
2026 |
2027 | case CROSSWALL:
2028 | switch (lev->wall_info & WM_MASK) {
2029 | case 0:
2030 | if (seenv == SV0)
2031 | idx = S_brcorn;
2032 | else if (seenv == SV2)
2033 | idx = S_blcorn;
2034 | else if (seenv == SV4)
2035 | idx = S_tlcorn;
2036 | else if (seenv == SV6)
2037 | idx = S_trcorn;
2038 | else if (!(seenv & ~(SV0|SV1|SV2)) &&
2039 | (seenv & SV1 || seenv == (SV0|SV2)))
2040 | idx = S_tuwall;
2041 | else if (!(seenv & ~(SV2|SV3|SV4)) &&
2042 | (seenv & SV3 || seenv == (SV2|SV4)))
2043 | idx = S_trwall;
2044 | else if (!(seenv & ~(SV4|SV5|SV6)) &&
2045 | (seenv & SV5 || seenv == (SV4|SV6)))
2046 | idx = S_tdwall;
2047 | else if (!(seenv & ~(SV0|SV6|SV7)) &&
2048 | (seenv & SV7 || seenv == (SV0|SV6)))
2049 | idx = S_tlwall;
2050 | else
2051 | idx = S_crwall;
2052 | break;
2053 |
2054 | case WM_X_TL:
2055 | row = cross_matrix[C_tl];
2056 | seenv = (seenv >> 4 | seenv << 4) & 0xff;
2057 | goto do_crwall;
2058 | case WM_X_TR:
2059 | row = cross_matrix[C_tr];
2060 | seenv = (seenv >> 6 | seenv << 2) & 0xff;
2061 | goto do_crwall;
2062 | case WM_X_BL:
2063 | row = cross_matrix[C_bl];
2064 | seenv = (seenv >> 2 | seenv << 6) & 0xff;
2065 | goto do_crwall;
2066 | case WM_X_BR:
2067 | row = cross_matrix[C_br];
2068 | do_crwall:
2069 | if (seenv == SV4)
2070 | idx = S_stone;
2071 | else {
2072 | seenv = seenv & ~SV4; /* strip SV4 */
2073 | if (seenv == SV0) {
2074 | col = C_brcorn;
2075 | } else if (seenv & (SV2|SV3)) {
2076 | if (seenv & (SV5|SV6|SV7))
2077 | col = C_crwall;
2078 | else if (seenv & (SV0|SV1))
2079 | col = C_tuwall;
2080 | else
2081 | col = C_blcorn;
2082 | } else if (seenv & (SV5|SV6)) {
2083 | if (seenv & (SV1|SV2|SV3))
2084 | col = C_crwall;
2085 | else if (seenv & (SV0|SV7))
2086 | col = C_tlwall;
2087 | else
2088 | col = C_trcorn;
2089 | } else if (seenv & SV1) {
2090 | col = seenv & SV7 ? C_crwall : C_tuwall;
2091 | } else if (seenv & SV7) {
2092 | col = seenv & SV1 ? C_crwall : C_tlwall;
2093 | } else {
2094 | impossible(
2095 | "wall_angle: bottom of crwall check");
2096 | col = C_crwall;
2097 | }
2098 |
2099 | idx = row[col];
2100 | }
2101 | break;
2102 |
2103 | case WM_X_TLBR:
2104 | if ( only(seenv, SV1|SV2|SV3) )
2105 | idx = S_blcorn;
2106 | else if ( only(seenv, SV5|SV6|SV7) )
2107 | idx = S_trcorn;
2108 | else if ( only(seenv, SV0|SV4) )
2109 | idx = S_stone;
2110 | else
2111 | idx = S_crwall;
2112 | break;
2113 |
2114 | case WM_X_BLTR:
2115 | if ( only(seenv, SV0|SV1|SV7) )
2116 | idx = S_brcorn;
2117 | else if ( only(seenv, SV3|SV4|SV5) )
2118 | idx = S_tlcorn;
2119 | else if ( only(seenv, SV2|SV6) )
2120 | idx = S_stone;
2121 | else
2122 | idx = S_crwall;
2123 | break;
2124 |
2125 | default:
2126 | impossible("wall_angle: unknown crosswall mode");
2127 | idx = S_stone;
2128 | break;
2129 | }
2130 | break;
2131 |
2132 | default:
2133 | impossible("wall_angle: unexpected wall type %d", lev->typ);
2134 | idx = S_stone;
2135 | }
2136 | return idx;
2137 | }
2138 |
2139 | /*display.c*/