xref: /netbsd-src/games/rogue/monster.c (revision 4fee23f98c45552038ad6b5bd05124a41302fb01)
1 /*	$NetBSD: monster.c,v 1.16 2009/10/19 02:34:40 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.16 2009/10/19 02:34:40 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