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 121STATIC_DCL void FDECL(display_monster,(XCHAR_P,XCHAR_P,struct monst *,int,XCHAR_P)); 122STATIC_DCL int FDECL(swallow_to_glyph, (int, int)); 123STATIC_DCL void FDECL(display_warning,(struct monst *)); 124 125STATIC_DCL int FDECL(check_pos, (int, int, int)); 126#ifdef WA_VERBOSE 127STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int)); 128#endif 129STATIC_DCL int FDECL(set_twall, (int,int, int,int, int,int, int,int)); 130STATIC_DCL int FDECL(set_wall, (int, int, int)); 131STATIC_DCL int FDECL(set_corn, (int,int, int,int, int,int, int,int)); 132STATIC_DCL int FDECL(set_crosswall, (int, int)); 133STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int)); 134STATIC_DCL void FDECL(t_warn, (struct rm *)); 135STATIC_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 */ 147struct obj * 148vobj_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 */ 167void 168magic_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 */ 212void 213map_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 */ 230void 231map_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 */ 249void 250map_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 */ 271void 272map_invisible(x, y) 273register xchar x, y; 274{ 275 if (x != u.ux || y != u.uy) { /* don't display I at hero's location */ 276 if (level.flags.hero_memory) 277 levl[x][y].glyph = GLYPH_INVISIBLE; 278 show_glyph(x, y, GLYPH_INVISIBLE); 279 } 280} 281 282/* 283 * unmap_object() 284 * 285 * Remove something from the map when the hero realizes it's not there any 286 * more. Replace it with background or known trap, but not with any other 287 * If this is used for detection, a full screen update is imminent anyway; 288 * if this is used to get rid of an invisible monster notation, we might have 289 * to call newsym(). 290 */ 291void 292unmap_object(x, y) 293 register int x, y; 294{ 295 register struct trap *trap; 296 297 if (!level.flags.hero_memory) return; 298 299 if ((trap = t_at(x,y)) != 0 && trap->tseen && !covers_traps(x,y)) 300 map_trap(trap, 0); 301 else if (levl[x][y].seenv) { 302 struct rm *lev = &levl[x][y]; 303 304 map_background(x, y, 0); 305 306 /* turn remembered dark room squares dark */ 307 if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) && 308 lev->typ == ROOM) 309 lev->glyph = cmap_to_glyph(S_stone); 310 } else 311 levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */ 312} 313 314 315/* 316 * map_location() 317 * 318 * Make whatever at this location show up. This is only for non-living 319 * things. This will not handle feeling invisible objects correctly. 320 * 321 * Internal to display.c, this is a #define for speed. 322 */ 323#define _map_location(x,y,show) \ 324{ \ 325 register struct obj *obj; \ 326 register struct trap *trap; \ 327 \ 328 if ((obj = vobj_at(x,y)) && !covers_objects(x,y)) \ 329 map_object(obj,show); \ 330 else if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y)) \ 331 map_trap(trap,show); \ 332 else \ 333 map_background(x,y,show); \ 334} 335 336void 337map_location(x,y,show) 338 int x, y, show; 339{ 340 _map_location(x,y,show); 341} 342 343#define DETECTED 2 344#define PHYSICALLY_SEEN 1 345#define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my))) 346 347/* 348 * display_monster() 349 * 350 * Note that this is *not* a map_XXXX() function! Monsters sort of float 351 * above everything. 352 * 353 * Yuck. Display body parts by recognizing that the display position is 354 * not the same as the monster position. Currently the only body part is 355 * a worm tail. 356 * 357 */ 358STATIC_OVL void 359display_monster(x, y, mon, sightflags, worm_tail) 360 register xchar x, y; /* display position */ 361 register struct monst *mon; /* monster to display */ 362 int sightflags; /* 1 if the monster is physically seen */ 363 /* 2 if detected using Detect_monsters */ 364 register xchar worm_tail; /* mon is actually a worm tail */ 365{ 366 register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING); 367 register int sensed = mon_mimic && 368 (Protection_from_shape_changers || sensemon(mon)); 369 /* 370 * We must do the mimic check first. If the mimic is mimicing something, 371 * and the location is in sight, we have to change the hero's memory 372 * so that when the position is out of sight, the hero remembers what 373 * the mimic was mimicing. 374 */ 375 376 if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) { 377 switch (mon->m_ap_type) { 378 default: 379 impossible("display_monster: bad m_ap_type value [ = %d ]", 380 (int) mon->m_ap_type); 381 case M_AP_NOTHING: 382 show_glyph(x, y, mon_to_glyph(mon)); 383 break; 384 385 case M_AP_FURNITURE: { 386 /* 387 * This is a poor man's version of map_background(). I can't 388 * use map_background() because we are overriding what is in 389 * the 'typ' field. Maybe have map_background()'s parameters 390 * be (x,y,glyph) instead of just (x,y). 391 * 392 * mappearance is currently set to an S_ index value in 393 * makemon.c. 394 */ 395 register int glyph = cmap_to_glyph(mon->mappearance); 396 levl[x][y].glyph = glyph; 397 if (!sensed) show_glyph(x,y, glyph); 398 break; 399 } 400 401 case M_AP_OBJECT: { 402 struct obj obj; /* Make a fake object to send */ 403 /* to map_object(). */ 404 obj.ox = x; 405 obj.oy = y; 406 obj.otyp = mon->mappearance; 407 obj.corpsenm = PM_TENGU; /* if mimicing a corpse */ 408 map_object(&obj,!sensed); 409 break; 410 } 411 412 case M_AP_MONSTER: 413 show_glyph(x,y, monnum_to_glyph(what_mon((int)mon->mappearance))); 414 break; 415 } 416 417 } 418 419 /* If the mimic is unsucessfully mimicing something, display the monster */ 420 if (!mon_mimic || sensed) { 421 int num; 422 423 /* [ALI] Only use detected glyphs when monster wouldn't be 424 * visible by any other means. 425 */ 426 if (sightflags == DETECTED) { 427 if (worm_tail) 428 num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); 429 else 430 num = detected_mon_to_glyph(mon); 431 } else if (mon->mtame && !Hallucination) { 432 if (worm_tail) 433 num = petnum_to_glyph(PM_LONG_WORM_TAIL); 434 else 435 num = pet_to_glyph(mon); 436 } else { 437 if (worm_tail) 438 num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); 439 else 440 num = mon_to_glyph(mon); 441 } 442 show_glyph(x,y,num); 443 } 444} 445 446/* 447 * display_warning() 448 * 449 * This is also *not* a map_XXXX() function! Monster warnings float 450 * above everything just like monsters do, but only if the monster 451 * is not showing. 452 * 453 * Do not call for worm tails. 454 */ 455STATIC_OVL void 456display_warning(mon) 457 register struct monst *mon; 458{ 459 int x = mon->mx, y = mon->my; 460 int wl = (int) (mon->m_lev / 4); 461 int glyph; 462 463 if (mon_warning(mon)) { 464 if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1; 465 /* 3.4.1: this really ought to be rn2(WARNCOUNT), but value "0" 466 isn't handled correctly by the what_is routine so avoid it */ 467 if (Hallucination) wl = rn1(WARNCOUNT-1,1); 468 glyph = warning_to_glyph(wl); 469 } else if (MATCH_WARN_OF_MON(mon)) { 470 glyph = mon_to_glyph(mon); 471 } else { 472 impossible("display_warning did not match warning type?"); 473 return; 474 } 475 show_glyph(x, y, glyph); 476} 477 478/* 479 * feel_location() 480 * 481 * Feel the given location. This assumes that the hero is blind and that 482 * the given position is either the hero's or one of the eight squares 483 * adjacent to the hero (except for a boulder push). 484 * If an invisible monster has gone away, that will be discovered. If an 485 * invisible monster has appeared, this will _not_ be discovered since 486 * searching only finds one monster per turn so we must check that separately. 487 */ 488void 489feel_location(x, y) 490 xchar x, y; 491{ 492 struct rm *lev = &(levl[x][y]); 493 struct obj *boulder; 494 register struct monst *mon; 495 496 /* If the hero's memory of an invisible monster is accurate, we want to keep 497 * him from detecting the same monster over and over again on each turn. 498 * We must return (so we don't erase the monster). (We must also, in the 499 * search function, be sure to skip over previously detected 'I's.) 500 */ 501 if (glyph_is_invisible(levl[x][y].glyph) && m_at(x,y)) return; 502 503 /* The hero can't feel non pool locations while under water. */ 504 if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(x,y)) 505 return; 506 507 /* Set the seen vector as if the hero had seen it. It doesn't matter */ 508 /* if the hero is levitating or not. */ 509 set_seenv(lev, u.ux, u.uy, x, y); 510 511 if (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) { 512 /* 513 * Levitation Rules. It is assumed that the hero can feel the state 514 * of the walls around herself and can tell if she is in a corridor, 515 * room, or doorway. Boulders are felt because they are large enough. 516 * Anything else is unknown because the hero can't reach the ground. 517 * This makes things difficult. 518 * 519 * Check (and display) in order: 520 * 521 * + Stone, walls, and closed doors. 522 * + Boulders. [see a boulder before a doorway] 523 * + Doors. 524 * + Room/water positions 525 * + Everything else (hallways!) 526 */ 527 if (IS_ROCK(lev->typ) || (IS_DOOR(lev->typ) && 528 (lev->doormask & (D_LOCKED | D_CLOSED)))) { 529 map_background(x, y, 1); 530 } else if ((boulder = sobj_at(BOULDER,x,y)) != 0) { 531 map_object(boulder, 1); 532 } else if (IS_DOOR(lev->typ)) { 533 map_background(x, y, 1); 534 } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) { 535 /* 536 * An open room or water location. Normally we wouldn't touch 537 * this, but we have to get rid of remembered boulder symbols. 538 * This will only occur in rare occations when the hero goes 539 * blind and doesn't find a boulder where expected (something 540 * came along and picked it up). We know that there is not a 541 * boulder at this location. Show fountains, pools, etc. 542 * underneath if already seen. Otherwise, show the appropriate 543 * floor symbol. 544 * 545 * Similarly, if the hero digs a hole in a wall or feels a location 546 * that used to contain an unseen monster. In these cases, 547 * there's no reason to assume anything was underneath, so 548 * just show the appropriate floor symbol. If something was 549 * embedded in the wall, the glyph will probably already 550 * reflect that. Don't change the symbol in this case. 551 * 552 * This isn't quite correct. If the boulder was on top of some 553 * other objects they should be seen once the boulder is removed. 554 * However, we have no way of knowing that what is there now 555 * was there then. So we let the hero have a lapse of memory. 556 * We could also just display what is currently on the top of the 557 * object stack (if anything). 558 */ 559 if (lev->glyph == objnum_to_glyph(BOULDER)) { 560 if (lev->typ != ROOM && lev->seenv) { 561 map_background(x, y, 1); 562 } else { 563 lev->glyph = lev->waslit ? cmap_to_glyph(S_room) : 564 cmap_to_glyph(S_stone); 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_room)) || 569 glyph_is_invisible(levl[x][y].glyph)) { 570 lev->glyph = lev->waslit ? cmap_to_glyph(S_room) : 571 cmap_to_glyph(S_stone); 572 show_glyph(x,y,lev->glyph); 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) && !lev->waslit) 611 show_glyph(x,y, lev->glyph = cmap_to_glyph(S_stone)); 612 else if (lev->typ == CORR && 613 lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit) 614 show_glyph(x,y, lev->glyph = cmap_to_glyph(S_corr)); 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 */ 628void 629newsym(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) { 756 if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR) 757 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr)); 758 else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM) 759 show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone)); 760 else 761 goto show_mem; 762 } else { 763show_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 */ 777void 778shieldeff(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 820static 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 828void 829tmp_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 */ 920void 921swallowed(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 */ 978void 979under_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 */ 1023void 1024under_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 */ 1064void 1065see_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 */ 1086void 1087set_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 */ 1109void 1110see_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 */ 1120void 1121see_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 */ 1136void 1137curs_on_u() 1138{ 1139 flush_screen(1); /* Flush waiting glyphs & put cursor on hero */ 1140} 1141 1142int 1143doredraw() 1144{ 1145 docrt(); 1146 return 0; 1147} 1148 1149void 1150docrt() 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 1201typedef struct { 1202 xchar new; /* perhaps move this bit into the rm strucure. */ 1203 int glyph; 1204} gbuf_entry; 1205 1206static gbuf_entry gbuf[ROWNO][COLNO]; 1207static char gbuf_start[ROWNO]; 1208static char gbuf_stop[ROWNO]; 1209 1210/* 1211 * Store the glyph in the 3rd screen for later flushing. 1212 */ 1213void 1214show_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 impossible("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 impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).", 1265 glyph, MAX_GLYPH, x, y); 1266 return; 1267 } 1268 1269 if (gbuf[y][x].glyph != glyph) { 1270 gbuf[y][x].glyph = glyph; 1271 gbuf[y][x].new = 1; 1272 if (gbuf_start[y] > x) gbuf_start[y] = x; 1273 if (gbuf_stop[y] < x) gbuf_stop[y] = x; 1274 } 1275} 1276 1277 1278/* 1279 * Reset the changed glyph borders so that none of the 3rd screen has 1280 * changed. 1281 */ 1282#define reset_glyph_bbox() \ 1283 { \ 1284 int i; \ 1285 \ 1286 for (i = 0; i < ROWNO; i++) { \ 1287 gbuf_start[i] = COLNO-1; \ 1288 gbuf_stop[i] = 0; \ 1289 } \ 1290 } 1291 1292 1293static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) }; 1294/* 1295 * Turn the 3rd screen into stone. 1296 */ 1297void 1298clear_glyph_buffer() 1299{ 1300 register int x, y; 1301 register gbuf_entry *gptr; 1302 1303 for (y = 0; y < ROWNO; y++) { 1304 gptr = &gbuf[y][0]; 1305 for (x = COLNO; x; x--) { 1306 *gptr++ = nul_gbuf; 1307 } 1308 } 1309 reset_glyph_bbox(); 1310} 1311 1312/* 1313 * Assumes that the indicated positions are filled with S_stone glyphs. 1314 */ 1315void 1316row_refresh(start,stop,y) 1317 int start,stop,y; 1318{ 1319 register int x; 1320 1321 for (x = start; x <= stop; x++) 1322 if (gbuf[y][x].glyph != cmap_to_glyph(S_stone)) 1323 print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph); 1324} 1325 1326void 1327cls() 1328{ 1329 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ 1330 flags.botlx = 1; /* force update of botl window */ 1331 clear_nhwindow(WIN_MAP); /* clear physical screen */ 1332 1333 clear_glyph_buffer(); /* this is sort of an extra effort, but OK */ 1334} 1335 1336/* 1337 * Synch the third screen with the display. 1338 */ 1339void 1340flush_screen(cursor_on_u) 1341 int cursor_on_u; 1342{ 1343 /* Prevent infinite loops on errors: 1344 * flush_screen->print_glyph->impossible->pline->flush_screen 1345 */ 1346 static boolean flushing = 0; 1347 static boolean delay_flushing = 0; 1348 register int x,y; 1349 1350 if (cursor_on_u == -1) delay_flushing = !delay_flushing; 1351 if (delay_flushing) return; 1352 if (flushing) return; /* if already flushing then return */ 1353 flushing = 1; 1354 1355 for (y = 0; y < ROWNO; y++) { 1356 register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]]; 1357 for (; x <= gbuf_stop[y]; gptr++, x++) 1358 if (gptr->new) { 1359 print_glyph(WIN_MAP,x,y,gptr->glyph); 1360 gptr->new = 0; 1361 } 1362 } 1363 1364 if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */ 1365 display_nhwindow(WIN_MAP, FALSE); 1366 reset_glyph_bbox(); 1367 flushing = 0; 1368 if(flags.botl || flags.botlx) bot(); 1369} 1370 1371/* ========================================================================= */ 1372 1373/* 1374 * back_to_glyph() 1375 * 1376 * Use the information in the rm structure at the given position to create 1377 * a glyph of a background. 1378 * 1379 * I had to add a field in the rm structure (horizontal) so that we knew 1380 * if open doors and secret doors were horizontal or vertical. Previously, 1381 * the screen symbol had the horizontal/vertical information set at 1382 * level generation time. 1383 * 1384 * I used the 'ladder' field (really doormask) for deciding if stairwells 1385 * were up or down. I didn't want to check the upstairs and dnstairs 1386 * variables. 1387 */ 1388int 1389back_to_glyph(x,y) 1390 xchar x,y; 1391{ 1392 int idx; 1393 struct rm *ptr = &(levl[x][y]); 1394 1395 switch (ptr->typ) { 1396 case SCORR: 1397 case STONE: 1398 idx = level.flags.arboreal ? S_tree : S_stone; 1399 break; 1400 case ROOM: idx = S_room; break; 1401 case CORR: 1402 idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr; 1403 break; 1404 case HWALL: 1405 case VWALL: 1406 case TLCORNER: 1407 case TRCORNER: 1408 case BLCORNER: 1409 case BRCORNER: 1410 case CROSSWALL: 1411 case TUWALL: 1412 case TDWALL: 1413 case TLWALL: 1414 case TRWALL: 1415 case SDOOR: 1416 idx = ptr->seenv ? wall_angle(ptr) : S_stone; 1417 break; 1418 case DOOR: 1419 if (ptr->doormask) { 1420 if (ptr->doormask & D_BROKEN) 1421 idx = S_ndoor; 1422 else if (ptr->doormask & D_ISOPEN) 1423 idx = (ptr->horizontal) ? S_hodoor : S_vodoor; 1424 else /* else is closed */ 1425 idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor; 1426 } else 1427 idx = S_ndoor; 1428 break; 1429 case IRONBARS: idx = S_bars; break; 1430 case TREE: idx = S_tree; break; 1431 case POOL: 1432 case MOAT: idx = S_pool; break; 1433 case STAIRS: 1434 idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair; 1435 break; 1436 case LADDER: 1437 idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder; 1438 break; 1439 case FOUNTAIN: idx = S_fountain; break; 1440 case SINK: idx = S_sink; break; 1441 case ALTAR: idx = S_altar; break; 1442 case GRAVE: idx = S_grave; break; 1443 case THRONE: idx = S_throne; break; 1444 case LAVAPOOL: idx = S_lava; break; 1445 case ICE: idx = S_ice; break; 1446 case AIR: idx = S_air; break; 1447 case CLOUD: idx = S_cloud; break; 1448 case WATER: idx = S_water; break; 1449 case DBWALL: 1450 idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge; 1451 break; 1452 case DRAWBRIDGE_UP: 1453 switch(ptr->drawbridgemask & DB_UNDER) { 1454 case DB_MOAT: idx = S_pool; break; 1455 case DB_LAVA: idx = S_lava; break; 1456 case DB_ICE: idx = S_ice; break; 1457 case DB_FLOOR: idx = S_room; break; 1458 default: 1459 impossible("Strange db-under: %d", 1460 ptr->drawbridgemask & DB_UNDER); 1461 idx = S_room; /* something is better than nothing */ 1462 break; 1463 } 1464 break; 1465 case DRAWBRIDGE_DOWN: 1466 idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge; 1467 break; 1468 default: 1469 impossible("back_to_glyph: unknown level type [ = %d ]",ptr->typ); 1470 idx = S_room; 1471 break; 1472 } 1473 1474 return cmap_to_glyph(idx); 1475} 1476 1477 1478/* 1479 * swallow_to_glyph() 1480 * 1481 * Convert a monster number and a swallow location into the correct glyph. 1482 * If you don't want a patchwork monster while hallucinating, decide on 1483 * a random monster in swallowed() and don't use what_mon() here. 1484 */ 1485STATIC_OVL int 1486swallow_to_glyph(mnum, loc) 1487 int mnum; 1488 int loc; 1489{ 1490 if (loc < S_sw_tl || S_sw_br < loc) { 1491 impossible("swallow_to_glyph: bad swallow location"); 1492 loc = S_sw_br; 1493 } 1494 return ((int) (what_mon(mnum)<<3) | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF; 1495} 1496 1497 1498 1499/* 1500 * zapdir_to_glyph() 1501 * 1502 * Change the given zap direction and beam type into a glyph. Each beam 1503 * type has four glyphs, one for each of the symbols below. The order of 1504 * the zap symbols [0-3] as defined in rm.h are: 1505 * 1506 * | S_vbeam ( 0, 1) or ( 0,-1) 1507 * - S_hbeam ( 1, 0) or (-1, 0) 1508 * \ S_lslant ( 1, 1) or (-1,-1) 1509 * / S_rslant (-1, 1) or ( 1,-1) 1510 */ 1511int 1512zapdir_to_glyph(dx, dy, beam_type) 1513 register int dx, dy; 1514 int beam_type; 1515{ 1516 if (beam_type >= NUM_ZAP) { 1517 impossible("zapdir_to_glyph: illegal beam type"); 1518 beam_type = 0; 1519 } 1520 dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0; 1521 1522 return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF; 1523} 1524 1525 1526/* 1527 * Utility routine for dowhatis() used to find out the glyph displayed at 1528 * the location. This isn't necessarily the same as the glyph in the levl 1529 * structure, so we must check the "third screen". 1530 */ 1531int 1532glyph_at(x, y) 1533 xchar x,y; 1534{ 1535 if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO) 1536 return cmap_to_glyph(S_room); /* XXX */ 1537 return gbuf[y][x].glyph; 1538} 1539 1540 1541/* ------------------------------------------------------------------------- */ 1542/* Wall Angle -------------------------------------------------------------- */ 1543 1544/*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */ 1545 1546#ifdef WA_VERBOSE 1547 1548static const char *FDECL(type_to_name, (int)); 1549static void FDECL(error4, (int,int,int,int,int,int)); 1550 1551static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */ 1552static const char *type_names[MAX_TYPE] = { 1553 "STONE", "VWALL", "HWALL", "TLCORNER", 1554 "TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL", 1555 "TUWALL", "TDWALL", "TLWALL", "TRWALL", 1556 "DBWALL", "SDOOR", "SCORR", "POOL", 1557 "MOAT", "WATER", "DRAWBRIDGE_UP","LAVAPOOL", 1558 "DOOR", "CORR", "ROOM", "STAIRS", 1559 "LADDER", "FOUNTAIN", "THRONE", "SINK", 1560 "ALTAR", "ICE", "DRAWBRIDGE_DOWN","AIR", 1561 "CLOUD" 1562}; 1563 1564 1565static const char * 1566type_to_name(type) 1567 int type; 1568{ 1569 return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type]; 1570} 1571 1572static void 1573error4(x, y, a, b, c, dd) 1574 int x, y, a, b, c, dd; 1575{ 1576 pline("set_wall_state: %s @ (%d,%d) %s%s%s%s", 1577 type_to_name(levl[x][y].typ), x, y, 1578 a ? "1":"", b ? "2":"", c ? "3":"", dd ? "4":""); 1579 bad_count[levl[x][y].typ]++; 1580} 1581#endif /* WA_VERBOSE */ 1582 1583/* 1584 * Return 'which' if position is implies an unfinshed exterior. Return 1585 * zero otherwise. Unfinished implies outer area is rock or a corridor. 1586 * 1587 * Things that are ambigious: lava 1588 */ 1589STATIC_OVL int 1590check_pos(x, y, which) 1591 int x, y, which; 1592{ 1593 int type; 1594 if (!isok(x,y)) return which; 1595 type = levl[x][y].typ; 1596 if (IS_ROCK(type) || type == CORR || type == SCORR) return which; 1597 return 0; 1598} 1599 1600/* Return TRUE if more than one is non-zero. */ 1601/*ARGSUSED*/ 1602#ifdef WA_VERBOSE 1603STATIC_OVL boolean 1604more_than_one(x, y, a, b, c) 1605 int x, y, a, b, c; 1606{ 1607 if ((a && (b|c)) || (b && (a|c)) || (c && (a|b))) { 1608 error4(x,y,a,b,c,0); 1609 return TRUE; 1610 } 1611 return FALSE; 1612} 1613#else 1614#define more_than_one(x, y, a, b, c) (((a) && ((b)|(c))) || ((b) && ((a)|(c))) || ((c) && ((a)|(b)))) 1615#endif 1616 1617/* Return the wall mode for a T wall. */ 1618STATIC_OVL int 1619set_twall(x0,y0, x1,y1, x2,y2, x3,y3) 1620int x0,y0, x1,y1, x2,y2, x3,y3; 1621{ 1622 int wmode, is_1, is_2, is_3; 1623 1624 is_1 = check_pos(x1, y1, WM_T_LONG); 1625 is_2 = check_pos(x2, y2, WM_T_BL); 1626 is_3 = check_pos(x3, y3, WM_T_BR); 1627 if (more_than_one(x0, y0, is_1, is_2, is_3)) { 1628 wmode = 0; 1629 } else { 1630 wmode = is_1 + is_2 + is_3; 1631 } 1632 return wmode; 1633} 1634 1635/* Return wall mode for a horizontal or vertical wall. */ 1636STATIC_OVL int 1637set_wall(x, y, horiz) 1638 int x, y, horiz; 1639{ 1640 int wmode, is_1, is_2; 1641 1642 if (horiz) { 1643 is_1 = check_pos(x,y-1, WM_W_TOP); 1644 is_2 = check_pos(x,y+1, WM_W_BOTTOM); 1645 } else { 1646 is_1 = check_pos(x-1,y, WM_W_LEFT); 1647 is_2 = check_pos(x+1,y, WM_W_RIGHT); 1648 } 1649 if (more_than_one(x, y, is_1, is_2, 0)) { 1650 wmode = 0; 1651 } else { 1652 wmode = is_1 + is_2; 1653 } 1654 return wmode; 1655} 1656 1657 1658/* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */ 1659STATIC_OVL int 1660set_corn(x1,y1, x2,y2, x3,y3, x4,y4) 1661 int x1, y1, x2, y2, x3, y3, x4, y4; 1662{ 1663 int wmode, is_1, is_2, is_3, is_4; 1664 1665 is_1 = check_pos(x1, y1, 1); 1666 is_2 = check_pos(x2, y2, 1); 1667 is_3 = check_pos(x3, y3, 1); 1668 is_4 = check_pos(x4, y4, 1); /* inner location */ 1669 1670 /* 1671 * All 4 should not be true. So if the inner location is rock, 1672 * use it. If all of the outer 3 are true, use outer. We currently 1673 * can't cover the case where only part of the outer is rock, so 1674 * we just say that all the walls are finished (if not overridden 1675 * by the inner section). 1676 */ 1677 if (is_4) { 1678 wmode = WM_C_INNER; 1679 } else if (is_1 && is_2 && is_3) 1680 wmode = WM_C_OUTER; 1681 else 1682 wmode = 0; /* finished walls on all sides */ 1683 1684 return wmode; 1685} 1686 1687/* Return mode for a crosswall. */ 1688STATIC_OVL int 1689set_crosswall(x, y) 1690 int x, y; 1691{ 1692 int wmode, is_1, is_2, is_3, is_4; 1693 1694 is_1 = check_pos(x-1, y-1, 1); 1695 is_2 = check_pos(x+1, y-1, 1); 1696 is_3 = check_pos(x+1, y+1, 1); 1697 is_4 = check_pos(x-1, y+1, 1); 1698 1699 wmode = is_1+is_2+is_3+is_4; 1700 if (wmode > 1) { 1701 if (is_1 && is_3 && (is_2+is_4 == 0)) { 1702 wmode = WM_X_TLBR; 1703 } else if (is_2 && is_4 && (is_1+is_3 == 0)) { 1704 wmode = WM_X_BLTR; 1705 } else { 1706#ifdef WA_VERBOSE 1707 error4(x,y,is_1,is_2,is_3,is_4); 1708#endif 1709 wmode = 0; 1710 } 1711 } else if (is_1) 1712 wmode = WM_X_TL; 1713 else if (is_2) 1714 wmode = WM_X_TR; 1715 else if (is_3) 1716 wmode = WM_X_BR; 1717 else if (is_4) 1718 wmode = WM_X_BL; 1719 1720 return wmode; 1721} 1722 1723/* Called from mklev. Scan the level and set the wall modes. */ 1724void 1725set_wall_state() 1726{ 1727 int x, y; 1728 int wmode; 1729 struct rm *lev; 1730 1731#ifdef WA_VERBOSE 1732 for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0; 1733#endif 1734 1735 for (x = 0; x < COLNO; x++) 1736 for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) { 1737 switch (lev->typ) { 1738 case SDOOR: 1739 wmode = set_wall(x, y, (int) lev->horizontal); 1740 break; 1741 case VWALL: 1742 wmode = set_wall(x, y, 0); 1743 break; 1744 case HWALL: 1745 wmode = set_wall(x, y, 1); 1746 break; 1747 case TDWALL: 1748 wmode = set_twall(x,y, x,y-1, x-1,y+1, x+1,y+1); 1749 break; 1750 case TUWALL: 1751 wmode = set_twall(x,y, x,y+1, x+1,y-1, x-1,y-1); 1752 break; 1753 case TLWALL: 1754 wmode = set_twall(x,y, x+1,y, x-1,y-1, x-1,y+1); 1755 break; 1756 case TRWALL: 1757 wmode = set_twall(x,y, x-1,y, x+1,y+1, x+1,y-1); 1758 break; 1759 case TLCORNER: 1760 wmode = set_corn(x-1,y-1, x,y-1, x-1,y, x+1,y+1); 1761 break; 1762 case TRCORNER: 1763 wmode = set_corn(x,y-1, x+1,y-1, x+1,y, x-1,y+1); 1764 break; 1765 case BLCORNER: 1766 wmode = set_corn(x,y+1, x-1,y+1, x-1,y, x+1,y-1); 1767 break; 1768 case BRCORNER: 1769 wmode = set_corn(x+1,y, x+1,y+1, x,y+1, x-1,y-1); 1770 break; 1771 case CROSSWALL: 1772 wmode = set_crosswall(x, y); 1773 break; 1774 1775 default: 1776 wmode = -1; /* don't set wall info */ 1777 break; 1778 } 1779 1780 if (wmode >= 0) 1781 lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode; 1782 } 1783 1784#ifdef WA_VERBOSE 1785 /* check if any bad positions found */ 1786 for (x = y = 0; x < MAX_TYPE; x++) 1787 if (bad_count[x]) { 1788 if (y == 0) { 1789 y = 1; /* only print once */ 1790 pline("set_wall_type: wall mode problems with: "); 1791 } 1792 pline("%s %d;", type_names[x], bad_count[x]); 1793 } 1794#endif /* WA_VERBOSE */ 1795} 1796 1797/* ------------------------------------------------------------------------- */ 1798/* This matrix is used here and in vision.c. */ 1799unsigned char seenv_matrix[3][3] = { {SV2, SV1, SV0}, 1800 {SV3, SVALL, SV7}, 1801 {SV4, SV5, SV6} }; 1802 1803#define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0)) 1804 1805/* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */ 1806STATIC_OVL void 1807set_seenv(lev, x0, y0, x, y) 1808 struct rm *lev; 1809 int x0, y0, x, y; /* from, to */ 1810{ 1811 int dx = x-x0, dy = y0-y; 1812 lev->seenv |= seenv_matrix[sign(dy)+1][sign(dx)+1]; 1813} 1814 1815/* ------------------------------------------------------------------------- */ 1816 1817/* T wall types, one for each row in wall_matrix[][]. */ 1818#define T_d 0 1819#define T_l 1 1820#define T_u 2 1821#define T_r 3 1822 1823/* 1824 * These are the column names of wall_matrix[][]. They are the "results" 1825 * of a tdwall pattern match. All T walls are rotated so they become 1826 * a tdwall. Then we do a single pattern match, but return the 1827 * correct result for the original wall by using different rows for 1828 * each of the wall types. 1829 */ 1830#define T_stone 0 1831#define T_tlcorn 1 1832#define T_trcorn 2 1833#define T_hwall 3 1834#define T_tdwall 4 1835 1836static const int wall_matrix[4][5] = { 1837 { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */ 1838 { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */ 1839 { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */ 1840 { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */ 1841}; 1842 1843 1844/* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][]. */ 1845#define C_bl 0 1846#define C_tl 1 1847#define C_tr 2 1848#define C_br 3 1849 1850/* 1851 * These are the column names for cross_matrix[][]. They express results 1852 * in C_br (bottom right) terms. All crosswalls with a single solid 1853 * quarter are rotated so the solid section is at the bottom right. 1854 * We pattern match on that, but return the correct result depending 1855 * on which row we'ere looking at. 1856 */ 1857#define C_trcorn 0 1858#define C_brcorn 1 1859#define C_blcorn 2 1860#define C_tlwall 3 1861#define C_tuwall 4 1862#define C_crwall 5 1863 1864static const int cross_matrix[4][6] = { 1865 { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall }, 1866 { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall }, 1867 { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall }, 1868 { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall }, 1869}; 1870 1871 1872/* Print out a T wall warning and all interesting info. */ 1873STATIC_OVL void 1874t_warn(lev) 1875 struct rm *lev; 1876{ 1877 static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x"; 1878 const char *wname; 1879 1880 if (lev->typ == TUWALL) wname = "tuwall"; 1881 else if (lev->typ == TLWALL) wname = "tlwall"; 1882 else if (lev->typ == TRWALL) wname = "trwall"; 1883 else if (lev->typ == TDWALL) wname = "tdwall"; 1884 else wname = "unknown"; 1885 impossible(warn_str, wname, lev->wall_info & WM_MASK, 1886 (unsigned int) lev->seenv); 1887} 1888 1889 1890/* 1891 * Return the correct graphics character index using wall type, wall mode, 1892 * and the seen vector. It is expected that seenv is non zero. 1893 * 1894 * All T-wall vectors are rotated to be TDWALL. All single crosswall 1895 * blocks are rotated to bottom right. All double crosswall are rotated 1896 * to W_X_BLTR. All results are converted back. 1897 * 1898 * The only way to understand this is to take out pen and paper and 1899 * draw diagrams. See rm.h for more details on the wall modes and 1900 * seen vector (SV). 1901 */ 1902STATIC_OVL int 1903wall_angle(lev) 1904 struct rm *lev; 1905{ 1906 register unsigned int seenv = lev->seenv & 0xff; 1907 const int *row; 1908 int col, idx; 1909 1910#define only(sv, bits) (((sv) & (bits)) && ! ((sv) & ~(bits))) 1911 switch (lev->typ) { 1912 case TUWALL: 1913 row = wall_matrix[T_u]; 1914 seenv = (seenv >> 4 | seenv << 4) & 0xff;/* rotate to tdwall */ 1915 goto do_twall; 1916 case TLWALL: 1917 row = wall_matrix[T_l]; 1918 seenv = (seenv >> 2 | seenv << 6) & 0xff;/* rotate to tdwall */ 1919 goto do_twall; 1920 case TRWALL: 1921 row = wall_matrix[T_r]; 1922 seenv = (seenv >> 6 | seenv << 2) & 0xff;/* rotate to tdwall */ 1923 goto do_twall; 1924 case TDWALL: 1925 row = wall_matrix[T_d]; 1926do_twall: 1927 switch (lev->wall_info & WM_MASK) { 1928 case 0: 1929 if (seenv == SV4) { 1930 col = T_tlcorn; 1931 } else if (seenv == SV6) { 1932 col = T_trcorn; 1933 } else if (seenv & (SV3|SV5|SV7) || 1934 ((seenv & SV4) && (seenv & SV6))) { 1935 col = T_tdwall; 1936 } else if (seenv & (SV0|SV1|SV2)) { 1937 col = (seenv & (SV4|SV6) ? T_tdwall : T_hwall); 1938 } else { 1939 t_warn(lev); 1940 col = T_stone; 1941 } 1942 break; 1943 case WM_T_LONG: 1944 if (seenv & (SV3|SV4) && !(seenv & (SV5|SV6|SV7))) { 1945 col = T_tlcorn; 1946 } else if (seenv&(SV6|SV7) && !(seenv&(SV3|SV4|SV5))) { 1947 col = T_trcorn; 1948 } else if ((seenv & SV5) || 1949 ((seenv & (SV3|SV4)) && (seenv & (SV6|SV7)))) { 1950 col = T_tdwall; 1951 } else { 1952 /* only SV0|SV1|SV2 */ 1953 if (! only(seenv, SV0|SV1|SV2) ) 1954 t_warn(lev); 1955 col = T_stone; 1956 } 1957 break; 1958 case WM_T_BL: 1959#if 0 /* older method, fixed */ 1960 if (only(seenv, SV4|SV5)) { 1961 col = T_tlcorn; 1962 } else if ((seenv & (SV0|SV1|SV2)) && 1963 only(seenv, SV0|SV1|SV2|SV6|SV7)) { 1964 col = T_hwall; 1965 } else if (seenv & SV3 || 1966 ((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) { 1967 col = T_tdwall; 1968 } else { 1969 if (seenv != SV6) 1970 t_warn(lev); 1971 col = T_stone; 1972 } 1973#endif /* 0 */ 1974 if (only(seenv, SV4|SV5)) 1975 col = T_tlcorn; 1976 else if ((seenv & (SV0|SV1|SV2|SV7)) && 1977 !(seenv & (SV3|SV4|SV5))) 1978 col = T_hwall; 1979 else if (only(seenv, SV6)) 1980 col = T_stone; 1981 else 1982 col = T_tdwall; 1983 break; 1984 case WM_T_BR: 1985#if 0 /* older method, fixed */ 1986 if (only(seenv, SV5|SV6)) { 1987 col = T_trcorn; 1988 } else if ((seenv & (SV0|SV1|SV2)) && 1989 only(seenv, SV0|SV1|SV2|SV3|SV4)) { 1990 col = T_hwall; 1991 } else if (seenv & SV7 || 1992 ((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) { 1993 col = T_tdwall; 1994 } else { 1995 if (seenv != SV4) 1996 t_warn(lev); 1997 col = T_stone; 1998 } 1999#endif /* 0 */ 2000 if (only(seenv, SV5|SV6)) 2001 col = T_trcorn; 2002 else if ((seenv & (SV0|SV1|SV2|SV3)) && 2003 !(seenv & (SV5|SV6|SV7))) 2004 col = T_hwall; 2005 else if (only(seenv, SV4)) 2006 col = T_stone; 2007 else 2008 col = T_tdwall; 2009 2010 break; 2011 default: 2012 impossible("wall_angle: unknown T wall mode %d", 2013 lev->wall_info & WM_MASK); 2014 col = T_stone; 2015 break; 2016 } 2017 idx = row[col]; 2018 break; 2019 2020 case SDOOR: 2021 if (lev->horizontal) goto horiz; 2022 /* fall through */ 2023 case VWALL: 2024 switch (lev->wall_info & WM_MASK) { 2025 case 0: idx = seenv ? S_vwall : S_stone; break; 2026 case 1: idx = seenv & (SV1|SV2|SV3|SV4|SV5) ? S_vwall : 2027 S_stone; 2028 break; 2029 case 2: idx = seenv & (SV0|SV1|SV5|SV6|SV7) ? S_vwall : 2030 S_stone; 2031 break; 2032 default: 2033 impossible("wall_angle: unknown vwall mode %d", 2034 lev->wall_info & WM_MASK); 2035 idx = S_stone; 2036 break; 2037 } 2038 break; 2039 2040 case HWALL: 2041horiz: 2042 switch (lev->wall_info & WM_MASK) { 2043 case 0: idx = seenv ? S_hwall : S_stone; break; 2044 case 1: idx = seenv & (SV3|SV4|SV5|SV6|SV7) ? S_hwall : 2045 S_stone; 2046 break; 2047 case 2: idx = seenv & (SV0|SV1|SV2|SV3|SV7) ? S_hwall : 2048 S_stone; 2049 break; 2050 default: 2051 impossible("wall_angle: unknown hwall mode %d", 2052 lev->wall_info & WM_MASK); 2053 idx = S_stone; 2054 break; 2055 } 2056 break; 2057 2058#define set_corner(idx, lev, which, outer, inner, name) \ 2059 switch ((lev)->wall_info & WM_MASK) { \ 2060 case 0: idx = which; break; \ 2061 case WM_C_OUTER: idx = seenv & (outer) ? which : S_stone; break; \ 2062 case WM_C_INNER: idx = seenv & ~(inner) ? which : S_stone; break; \ 2063 default: \ 2064 impossible("wall_angle: unknown %s mode %d", name, \ 2065 (lev)->wall_info & WM_MASK); \ 2066 idx = S_stone; \ 2067 break; \ 2068 } 2069 2070 case TLCORNER: 2071 set_corner(idx, lev, S_tlcorn, (SV3|SV4|SV5), SV4, "tlcorn"); 2072 break; 2073 case TRCORNER: 2074 set_corner(idx, lev, S_trcorn, (SV5|SV6|SV7), SV6, "trcorn"); 2075 break; 2076 case BLCORNER: 2077 set_corner(idx, lev, S_blcorn, (SV1|SV2|SV3), SV2, "blcorn"); 2078 break; 2079 case BRCORNER: 2080 set_corner(idx, lev, S_brcorn, (SV7|SV0|SV1), SV0, "brcorn"); 2081 break; 2082 2083 2084 case CROSSWALL: 2085 switch (lev->wall_info & WM_MASK) { 2086 case 0: 2087 if (seenv == SV0) 2088 idx = S_brcorn; 2089 else if (seenv == SV2) 2090 idx = S_blcorn; 2091 else if (seenv == SV4) 2092 idx = S_tlcorn; 2093 else if (seenv == SV6) 2094 idx = S_trcorn; 2095 else if (!(seenv & ~(SV0|SV1|SV2)) && 2096 (seenv & SV1 || seenv == (SV0|SV2))) 2097 idx = S_tuwall; 2098 else if (!(seenv & ~(SV2|SV3|SV4)) && 2099 (seenv & SV3 || seenv == (SV2|SV4))) 2100 idx = S_trwall; 2101 else if (!(seenv & ~(SV4|SV5|SV6)) && 2102 (seenv & SV5 || seenv == (SV4|SV6))) 2103 idx = S_tdwall; 2104 else if (!(seenv & ~(SV0|SV6|SV7)) && 2105 (seenv & SV7 || seenv == (SV0|SV6))) 2106 idx = S_tlwall; 2107 else 2108 idx = S_crwall; 2109 break; 2110 2111 case WM_X_TL: 2112 row = cross_matrix[C_tl]; 2113 seenv = (seenv >> 4 | seenv << 4) & 0xff; 2114 goto do_crwall; 2115 case WM_X_TR: 2116 row = cross_matrix[C_tr]; 2117 seenv = (seenv >> 6 | seenv << 2) & 0xff; 2118 goto do_crwall; 2119 case WM_X_BL: 2120 row = cross_matrix[C_bl]; 2121 seenv = (seenv >> 2 | seenv << 6) & 0xff; 2122 goto do_crwall; 2123 case WM_X_BR: 2124 row = cross_matrix[C_br]; 2125do_crwall: 2126 if (seenv == SV4) 2127 idx = S_stone; 2128 else { 2129 seenv = seenv & ~SV4; /* strip SV4 */ 2130 if (seenv == SV0) { 2131 col = C_brcorn; 2132 } else if (seenv & (SV2|SV3)) { 2133 if (seenv & (SV5|SV6|SV7)) 2134 col = C_crwall; 2135 else if (seenv & (SV0|SV1)) 2136 col = C_tuwall; 2137 else 2138 col = C_blcorn; 2139 } else if (seenv & (SV5|SV6)) { 2140 if (seenv & (SV1|SV2|SV3)) 2141 col = C_crwall; 2142 else if (seenv & (SV0|SV7)) 2143 col = C_tlwall; 2144 else 2145 col = C_trcorn; 2146 } else if (seenv & SV1) { 2147 col = seenv & SV7 ? C_crwall : C_tuwall; 2148 } else if (seenv & SV7) { 2149 col = seenv & SV1 ? C_crwall : C_tlwall; 2150 } else { 2151 impossible( 2152 "wall_angle: bottom of crwall check"); 2153 col = C_crwall; 2154 } 2155 2156 idx = row[col]; 2157 } 2158 break; 2159 2160 case WM_X_TLBR: 2161 if ( only(seenv, SV1|SV2|SV3) ) 2162 idx = S_blcorn; 2163 else if ( only(seenv, SV5|SV6|SV7) ) 2164 idx = S_trcorn; 2165 else if ( only(seenv, SV0|SV4) ) 2166 idx = S_stone; 2167 else 2168 idx = S_crwall; 2169 break; 2170 2171 case WM_X_BLTR: 2172 if ( only(seenv, SV0|SV1|SV7) ) 2173 idx = S_brcorn; 2174 else if ( only(seenv, SV3|SV4|SV5) ) 2175 idx = S_tlcorn; 2176 else if ( only(seenv, SV2|SV6) ) 2177 idx = S_stone; 2178 else 2179 idx = S_crwall; 2180 break; 2181 2182 default: 2183 impossible("wall_angle: unknown crosswall mode"); 2184 idx = S_stone; 2185 break; 2186 } 2187 break; 2188 2189 default: 2190 impossible("wall_angle: unexpected wall type %d", lev->typ); 2191 idx = S_stone; 2192 } 2193 return idx; 2194} 2195 2196/*display.c*/ 2197