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*/