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