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