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