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