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