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