1 /*- 2 * Copyright (c) 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Timothy C. Stoehr. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)monster.c 8.1 (Berkeley) 5/31/93 33 * $FreeBSD: src/games/rogue/monster.c,v 1.6 1999/11/30 03:49:24 billf Exp $ 34 */ 35 36 /* 37 * monster.c 38 * 39 * This source herein may be modified and/or distributed by anybody who 40 * so desires, with the following restrictions: 41 * 1.) No portion of this notice shall be removed. 42 * 2.) Credit shall not be taken for the creation of this source. 43 * 3.) This code is not to be traded, sold, or used for personal 44 * gain or profit. 45 * 46 */ 47 48 #include "rogue.h" 49 50 object level_monsters; 51 boolean mon_disappeared; 52 53 const char *const m_names[] = { 54 "aquator", 55 "bat", 56 "centaur", 57 "dragon", 58 "emu", 59 "venus fly-trap", 60 "griffin", 61 "hobgoblin", 62 "ice monster", 63 "jabberwock", 64 "kestrel", 65 "leprechaun", 66 "medusa", 67 "nymph", 68 "orc", 69 "phantom", 70 "quagga", 71 "rattlesnake", 72 "snake", 73 "troll", 74 "black unicorn", 75 "vampire", 76 "wraith", 77 "xeroc", 78 "yeti", 79 "zombie" 80 }; 81 82 static object mon_tab[MONSTERS] = { 83 {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 84 {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 85 {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 86 {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 87 {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 88 {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 89 {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G', 90 2000,20,126,85,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 91 {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 92 {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 93 {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 94 {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 95 {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 96 {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M', 97 250,18,126,85,0,25,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 98 {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 99 {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 100 {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 101 {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 102 {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 103 {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 104 {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 105 {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U', 106 200,17,26,85,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 107 {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V', 108 350,19,126,85,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 109 {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 110 {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 111 {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}, 112 {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL} 113 }; 114 115 static void aim_monster(object *); 116 static boolean flit(object *); 117 static boolean move_confused(object *); 118 static boolean mtry(object *, short, short); 119 static boolean no_room_for_monster(int); 120 static void put_m_at(short, short, object *); 121 static short rogue_is_around(int, int); 122 123 void 124 put_mons(void) 125 { 126 short i; 127 short n; 128 object *monster; 129 short row, col; 130 131 n = get_rand(4, 6); 132 133 for (i = 0; i < n; i++) { 134 monster = gr_monster(NULL, 0); 135 if ((monster->m_flags & WANDERS) && coin_toss()) { 136 wake_up(monster); 137 } 138 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); 139 put_m_at(row, col, monster); 140 } 141 } 142 143 object * 144 gr_monster(object *monster, int mn) 145 { 146 if (!monster) { 147 monster = alloc_object(); 148 149 for (;;) { 150 mn = get_rand(0, MONSTERS-1); 151 if ((cur_level >= mon_tab[mn].first_level) && 152 (cur_level <= mon_tab[mn].last_level)) { 153 break; 154 } 155 } 156 } 157 *monster = mon_tab[mn]; 158 if (monster->m_flags & IMITATES) { 159 monster->disguise = gr_obj_char(); 160 } 161 if (cur_level > (AMULET_LEVEL + 2)) { 162 monster->m_flags |= HASTED; 163 } 164 monster->trow = NO_ROOM; 165 return(monster); 166 } 167 168 void 169 mv_mons(void) 170 { 171 object *monster, *next_monster, *test_mons; 172 boolean flew; 173 174 if (haste_self % 2) { 175 return; 176 } 177 178 monster = level_monsters.next_monster; 179 180 while (monster) { 181 next_monster = monster->next_monster; 182 mon_disappeared = 0; 183 if (monster->m_flags & HASTED) { 184 mv_1_monster(monster, rogue.row, rogue.col); 185 if (mon_disappeared) { 186 goto NM; 187 } 188 } else if (monster->m_flags & SLOWED) { 189 monster->slowed_toggle = !monster->slowed_toggle; 190 if (monster->slowed_toggle) { 191 goto NM; 192 } 193 } 194 if ((monster->m_flags & CONFUSED) && move_confused(monster)) { 195 goto NM; 196 } 197 flew = 0; 198 if ( (monster->m_flags & FLIES) && 199 !(monster->m_flags & NAPPING) && 200 !mon_can_go(monster, rogue.row, rogue.col)) { 201 flew = 1; 202 mv_1_monster(monster, rogue.row, rogue.col); 203 if (mon_disappeared) { 204 goto NM; 205 } 206 } 207 if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) { 208 mv_1_monster(monster, rogue.row, rogue.col); 209 } 210 NM: test_mons = level_monsters.next_monster; 211 monster = NULL; 212 while (test_mons) 213 { 214 if (next_monster == test_mons) 215 { 216 monster = next_monster; 217 break; 218 } 219 test_mons = test_mons -> next_monster; 220 } 221 } 222 } 223 224 void 225 party_monsters(int rn, int n) 226 { 227 short i, j; 228 short row, col; 229 object *monster; 230 boolean found; 231 232 row = col = 0; 233 n += n; 234 235 for (i = 0; i < MONSTERS; i++) { 236 mon_tab[i].first_level -= (cur_level % 3); 237 } 238 for (i = 0; i < n; i++) { 239 if (no_room_for_monster(rn)) { 240 break; 241 } 242 for (j = found = 0; ((!found) && (j < 250)); j++) { 243 row = get_rand(rooms[rn].top_row+1, 244 rooms[rn].bottom_row-1); 245 col = get_rand(rooms[rn].left_col+1, 246 rooms[rn].right_col-1); 247 if ((!(dungeon[row][col] & MONSTER)) && 248 (dungeon[row][col] & (FLOOR | TUNNEL))) { 249 found = 1; 250 } 251 } 252 if (found) { 253 monster = gr_monster(NULL, 0); 254 if (!(monster->m_flags & IMITATES)) { 255 monster->m_flags |= WAKENS; 256 } 257 put_m_at(row, col, monster); 258 } 259 } 260 for (i = 0; i < MONSTERS; i++) { 261 mon_tab[i].first_level += (cur_level % 3); 262 } 263 } 264 265 short 266 gmc_row_col(int row, int col) 267 { 268 object *monster; 269 270 if ((monster = object_at(&level_monsters, row, col)) != NULL) { 271 if ((!(detect_monster || see_invisible || r_see_invisible) && 272 (monster->m_flags & INVISIBLE)) || blind) { 273 return(monster->trail_char); 274 } 275 if (monster->m_flags & IMITATES) { 276 return(monster->disguise); 277 } 278 return(monster->m_char); 279 } else { 280 return('&'); /* BUG if this ever happens */ 281 } 282 } 283 284 short 285 gmc(object *monster) 286 { 287 if ((!(detect_monster || see_invisible || r_see_invisible) && 288 (monster->m_flags & INVISIBLE)) 289 || blind) { 290 return(monster->trail_char); 291 } 292 if (monster->m_flags & IMITATES) { 293 return(monster->disguise); 294 } 295 return(monster->m_char); 296 } 297 298 void 299 mv_1_monster(object *monster, short row, short col) 300 { 301 short i, n; 302 boolean tried[6]; 303 304 if (monster->m_flags & ASLEEP) { 305 if (monster->m_flags & NAPPING) { 306 if (--monster->nap_length <= 0) { 307 monster->m_flags &= (~(NAPPING | ASLEEP)); 308 } 309 return; 310 } 311 if ((monster->m_flags & WAKENS) && 312 rogue_is_around(monster->row, monster->col) && 313 rand_percent(((stealthy > 0) ? 314 (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) : 315 WAKE_PERCENT))) { 316 wake_up(monster); 317 } 318 return; 319 } else if (monster->m_flags & ALREADY_MOVED) { 320 monster->m_flags &= (~ALREADY_MOVED); 321 return; 322 } 323 if ((monster->m_flags & FLITS) && flit(monster)) { 324 return; 325 } 326 if ((monster->m_flags & STATIONARY) && 327 (!mon_can_go(monster, rogue.row, rogue.col))) { 328 return; 329 } 330 if (monster->m_flags & FREEZING_ROGUE) { 331 return; 332 } 333 if ((monster->m_flags & CONFUSES) && m_confuse(monster)) { 334 return; 335 } 336 if (mon_can_go(monster, rogue.row, rogue.col)) { 337 mon_hit(monster); 338 return; 339 } 340 if ((monster->m_flags & FLAMES) && flame_broil(monster)) { 341 return; 342 } 343 if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) { 344 return; 345 } 346 if ((monster->trow == monster->row) && 347 (monster->tcol == monster->col)) { 348 monster->trow = NO_ROOM; 349 } else if (monster->trow != NO_ROOM) { 350 row = monster->trow; 351 col = monster->tcol; 352 } 353 if (monster->row > row) { 354 row = monster->row - 1; 355 } else if (monster->row < row) { 356 row = monster->row + 1; 357 } 358 if ((dungeon[row][monster->col] & DOOR) && 359 mtry(monster, row, monster->col)) { 360 return; 361 } 362 if (monster->col > col) { 363 col = monster->col - 1; 364 } else if (monster->col < col) { 365 col = monster->col + 1; 366 } 367 if ((dungeon[monster->row][col] & DOOR) && 368 mtry(monster, monster->row, col)) { 369 return; 370 } 371 if (mtry(monster, row, col)) { 372 return; 373 } 374 375 for (i = 0; i <= 5; i++) tried[i] = 0; 376 377 for (i = 0; i < 6; i++) { 378 NEXT_TRY: n = get_rand(0, 5); 379 switch(n) { 380 case 0: 381 if (!tried[n] && mtry(monster, row, monster->col-1)) { 382 goto O; 383 } 384 break; 385 case 1: 386 if (!tried[n] && mtry(monster, row, monster->col)) { 387 goto O; 388 } 389 break; 390 case 2: 391 if (!tried[n] && mtry(monster, row, monster->col+1)) { 392 goto O; 393 } 394 break; 395 case 3: 396 if (!tried[n] && mtry(monster, monster->row-1, col)) { 397 goto O; 398 } 399 break; 400 case 4: 401 if (!tried[n] && mtry(monster, monster->row, col)) { 402 goto O; 403 } 404 break; 405 case 5: 406 if (!tried[n] && mtry(monster, monster->row+1, col)) { 407 goto O; 408 } 409 break; 410 } 411 if (!tried[n]) { 412 tried[n] = 1; 413 } else { 414 goto NEXT_TRY; 415 } 416 } 417 O: 418 if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) { 419 if (++(monster->o) > 4) { 420 if ((monster->trow == NO_ROOM) && 421 (!mon_sees(monster, rogue.row, rogue.col))) { 422 monster->trow = get_rand(1, (DROWS - 2)); 423 monster->tcol = get_rand(0, (DCOLS - 1)); 424 } else { 425 monster->trow = NO_ROOM; 426 monster->o = 0; 427 } 428 } 429 } else { 430 monster->o_row = monster->row; 431 monster->o_col = monster->col; 432 monster->o = 0; 433 } 434 } 435 436 static boolean 437 mtry(object *monster, short row, short col) 438 { 439 if (mon_can_go(monster, row, col)) { 440 move_mon_to(monster, row, col); 441 return(1); 442 } 443 return(0); 444 } 445 446 void 447 move_mon_to(object *monster, short row, short col) 448 { 449 short c; 450 int mrow, mcol; 451 452 mrow = monster->row; 453 mcol = monster->col; 454 455 dungeon[mrow][mcol] &= ~MONSTER; 456 dungeon[row][col] |= MONSTER; 457 458 c = mvinch(mrow, mcol); 459 460 if ((c >= 'A') && (c <= 'Z')) { 461 if (!detect_monster) { 462 mvaddch(mrow, mcol, monster->trail_char); 463 } else { 464 if (rogue_can_see(mrow, mcol)) { 465 mvaddch(mrow, mcol, monster->trail_char); 466 } else { 467 if (monster->trail_char == '.') { 468 monster->trail_char = ' '; 469 } 470 mvaddch(mrow, mcol, monster->trail_char); 471 } 472 } 473 } 474 monster->trail_char = mvinch(row, col); 475 if (!blind && (detect_monster || rogue_can_see(row, col))) { 476 if ((!(monster->m_flags & INVISIBLE) || 477 (detect_monster || see_invisible || r_see_invisible))) { 478 mvaddch(row, col, gmc(monster)); 479 } 480 } 481 if ((dungeon[row][col] & DOOR) && 482 (get_room_number(row, col) != cur_room) && 483 (dungeon[mrow][mcol] == FLOOR) && !blind) { 484 mvaddch(mrow, mcol, ' '); 485 } 486 if (dungeon[row][col] & DOOR) { 487 dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0), 488 row, col); 489 } else { 490 monster->row = row; 491 monster->col = col; 492 } 493 } 494 495 boolean 496 mon_can_go(const object *monster, short row, short col) 497 { 498 object *obj; 499 short dr, dc; 500 501 dr = monster->row - row; /* check if move distance > 1 */ 502 if ((dr >= 2) || (dr <= -2)) { 503 return(0); 504 } 505 dc = monster->col - col; 506 if ((dc >= 2) || (dc <= -2)) { 507 return(0); 508 } 509 if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) { 510 return(0); 511 } 512 if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) { 513 return(0); 514 } 515 if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) || 516 (dungeon[monster->row][monster->col]&DOOR))) { 517 return(0); 518 } 519 if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) && 520 (monster->trow == NO_ROOM)) { 521 if ((monster->row < rogue.row) && (row < monster->row)) return(0); 522 if ((monster->row > rogue.row) && (row > monster->row)) return(0); 523 if ((monster->col < rogue.col) && (col < monster->col)) return(0); 524 if ((monster->col > rogue.col) && (col > monster->col)) return(0); 525 } 526 if (dungeon[row][col] & OBJECT) { 527 obj = object_at(&level_objects, row, col); 528 if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) { 529 return(0); 530 } 531 } 532 return(1); 533 } 534 535 void 536 wake_up(object *monster) 537 { 538 if (!(monster->m_flags & NAPPING)) { 539 monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS)); 540 } 541 } 542 543 void 544 wake_room(short rn, boolean entering, short row, short col) 545 { 546 object *monster; 547 short wake_percent; 548 boolean in_room; 549 550 wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT; 551 if (stealthy > 0) { 552 wake_percent /= (STEALTH_FACTOR + stealthy); 553 } 554 555 monster = level_monsters.next_monster; 556 557 while (monster) { 558 in_room = (rn == get_room_number(monster->row, monster->col)); 559 if (in_room) { 560 if (entering) { 561 monster->trow = NO_ROOM; 562 } else { 563 monster->trow = row; 564 monster->tcol = col; 565 } 566 } 567 if ((monster->m_flags & WAKENS) && 568 (rn == get_room_number(monster->row, monster->col))) { 569 if (rand_percent(wake_percent)) { 570 wake_up(monster); 571 } 572 } 573 monster = monster->next_monster; 574 } 575 } 576 577 const char * 578 mon_name(const object *monster) 579 { 580 short ch; 581 582 if (blind || ((monster->m_flags & INVISIBLE) && 583 !(detect_monster || see_invisible || r_see_invisible))) { 584 return("something"); 585 } 586 if (halluc) { 587 ch = get_rand('A', 'Z') - 'A'; 588 return(m_names[ch]); 589 } 590 ch = monster->m_char - 'A'; 591 return(m_names[ch]); 592 } 593 594 static short 595 rogue_is_around(int row, int col) 596 { 597 short rdif, cdif, retval; 598 599 rdif = row - rogue.row; 600 cdif = col - rogue.col; 601 602 retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); 603 return(retval); 604 } 605 606 void 607 wanderer(void) 608 { 609 object *monster; 610 short row, col, i; 611 boolean found = 0; 612 613 for (i = 0; ((i < 15) && (!found)); i++) { 614 monster = gr_monster(NULL, 0); 615 if (!(monster->m_flags & (WAKENS | WANDERS))) { 616 free_object(monster); 617 } else { 618 found = 1; 619 } 620 } 621 if (found) { 622 found = 0; 623 wake_up(monster); 624 for (i = 0; ((i < 25) && (!found)); i++) { 625 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); 626 if (!rogue_can_see(row, col)) { 627 put_m_at(row, col, monster); 628 found = 1; 629 } 630 } 631 if (!found) { 632 free_object(monster); 633 } 634 } 635 } 636 637 void 638 show_monsters(void) 639 { 640 object *monster; 641 642 detect_monster = 1; 643 644 if (blind) { 645 return; 646 } 647 monster = level_monsters.next_monster; 648 649 while (monster) { 650 mvaddch(monster->row, monster->col, monster->m_char); 651 if (monster->m_flags & IMITATES) { 652 monster->m_flags &= (~IMITATES); 653 monster->m_flags |= WAKENS; 654 } 655 monster = monster->next_monster; 656 } 657 } 658 659 void 660 create_monster(void) 661 { 662 short row, col; 663 short i; 664 boolean found = 0; 665 object *monster; 666 667 row = rogue.row; 668 col = rogue.col; 669 670 for (i = 0; i < 9; i++) { 671 rand_around(i, &row, &col); 672 if (((row == rogue.row) && (col == rogue.col)) || 673 (row < MIN_ROW) || (row > (DROWS-2)) || 674 (col < 0) || (col > (DCOLS-1))) { 675 continue; 676 } 677 if ((!(dungeon[row][col] & MONSTER)) && 678 (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) { 679 found = 1; 680 break; 681 } 682 } 683 if (found) { 684 monster = gr_monster(NULL, 0); 685 put_m_at(row, col, monster); 686 mvaddch(row, col, gmc(monster)); 687 if (monster->m_flags & (WANDERS | WAKENS)) { 688 wake_up(monster); 689 } 690 } else { 691 message("you hear a faint cry of anguish in the distance", 0); 692 } 693 } 694 695 static void 696 put_m_at(short row, short col, object *monster) 697 { 698 monster->row = row; 699 monster->col = col; 700 dungeon[row][col] |= MONSTER; 701 monster->trail_char = mvinch(row, col); 702 add_to_pack(monster, &level_monsters, 0); 703 aim_monster(monster); 704 } 705 706 static void 707 aim_monster(object *monster) 708 { 709 short i, rn, d, r; 710 711 rn = get_room_number(monster->row, monster->col); 712 if (rn == NO_ROOM) 713 clean_up("aim_monster: monster not in room"); 714 r = get_rand(0, 12); 715 716 for (i = 0; i < 4; i++) { 717 d = (r + i) % 4; 718 if (rooms[rn].doors[d].oth_room != NO_ROOM) { 719 monster->trow = rooms[rn].doors[d].door_row; 720 monster->tcol = rooms[rn].doors[d].door_col; 721 break; 722 } 723 } 724 } 725 726 int 727 rogue_can_see(int row, int col) 728 { 729 int retval; 730 731 retval = !blind && 732 (((get_room_number(row, col) == cur_room) && 733 !(rooms[cur_room].is_room & R_MAZE)) || 734 rogue_is_around(row, col)); 735 736 return(retval); 737 } 738 739 static boolean 740 move_confused(object *monster) 741 { 742 short i, row, col; 743 744 if (!(monster->m_flags & ASLEEP)) { 745 if (--monster->moves_confused <= 0) { 746 monster->m_flags &= (~CONFUSED); 747 } 748 if (monster->m_flags & STATIONARY) { 749 return(coin_toss() ? 1 : 0); 750 } else if (rand_percent(15)) { 751 return(1); 752 } 753 row = monster->row; 754 col = monster->col; 755 756 for (i = 0; i < 9; i++) { 757 rand_around(i, &row, &col); 758 if ((row == rogue.row) && (col == rogue.col)) { 759 return(0); 760 } 761 if (mtry(monster, row, col)) { 762 return(1); 763 } 764 } 765 } 766 return(0); 767 } 768 769 static boolean 770 flit(object *monster) 771 { 772 short i, row, col; 773 774 if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) { 775 return(0); 776 } 777 if (rand_percent(10)) { 778 return(1); 779 } 780 row = monster->row; 781 col = monster->col; 782 783 for (i = 0; i < 9; i++) { 784 rand_around(i, &row, &col); 785 if ((row == rogue.row) && (col == rogue.col)) { 786 continue; 787 } 788 if (mtry(monster, row, col)) { 789 return(1); 790 } 791 } 792 return(1); 793 } 794 795 char 796 gr_obj_char(void) 797 { 798 short r; 799 const char *rs = "%!?]=/):*"; 800 801 r = get_rand(0, 8); 802 803 return(rs[r]); 804 } 805 806 static boolean 807 no_room_for_monster(int rn) 808 { 809 short i, j; 810 811 for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { 812 for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { 813 if (!(dungeon[i][j] & MONSTER)) { 814 return(0); 815 } 816 } 817 } 818 return(1); 819 } 820 821 void 822 aggravate(void) 823 { 824 object *monster; 825 826 message("you hear a high pitched humming noise", 0); 827 828 monster = level_monsters.next_monster; 829 830 while (monster) { 831 wake_up(monster); 832 monster->m_flags &= (~IMITATES); 833 if (rogue_can_see(monster->row, monster->col)) { 834 mvaddch(monster->row, monster->col, monster->m_char); 835 } 836 monster = monster->next_monster; 837 } 838 } 839 840 boolean 841 mon_sees(const object *monster, int row, int col) 842 { 843 short rn, rdif, cdif, retval; 844 845 rn = get_room_number(row, col); 846 847 if ( (rn != NO_ROOM) && 848 (rn == get_room_number(monster->row, monster->col)) && 849 !(rooms[rn].is_room & R_MAZE)) { 850 return(1); 851 } 852 rdif = row - monster->row; 853 cdif = col - monster->col; 854 855 retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); 856 return(retval); 857 } 858 859 void 860 mv_aquatars(void) 861 { 862 object *monster; 863 864 monster = level_monsters.next_monster; 865 866 while (monster) { 867 if ((monster->m_char == 'A') && 868 mon_can_go(monster, rogue.row, rogue.col)) { 869 mv_1_monster(monster, rogue.row, rogue.col); 870 monster->m_flags |= ALREADY_MOVED; 871 } 872 monster = monster->next_monster; 873 } 874 } 875