xref: /dragonfly/games/rogue/monster.c (revision 9a92bb4c)
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. 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  * @(#)monster.c	8.1 (Berkeley) 5/31/93
37  * $FreeBSD: src/games/rogue/monster.c,v 1.6 1999/11/30 03:49:24 billf Exp $
38  * $DragonFly: src/games/rogue/monster.c,v 1.3 2006/09/02 19:31:07 pavalos Exp $
39  */
40 
41 /*
42  * monster.c
43  *
44  * This source herein may be modified and/or distributed by anybody who
45  * so desires, with the following restrictions:
46  *    1.)  No portion of this notice shall be removed.
47  *    2.)  Credit shall not be taken for the creation of this source.
48  *    3.)  This code is not to be traded, sold, or used for personal
49  *         gain or profit.
50  *
51  */
52 
53 #include "rogue.h"
54 
55 static boolean	mtry(object *, short, short);
56 static short	rogue_is_around(int, int);
57 static void	put_m_at(short, short, object *);
58 static void	aim_monster(object *);
59 static boolean	move_confused(object *);
60 static boolean	flit(object *);
61 static boolean	no_room_for_monster(int);
62 
63 object level_monsters;
64 boolean mon_disappeared;
65 
66 const char *const m_names[] = {
67 	"aquator",
68 	"bat",
69 	"centaur",
70 	"dragon",
71 	"emu",
72 	"venus fly-trap",
73 	"griffin",
74 	"hobgoblin",
75 	"ice monster",
76 	"jabberwock",
77 	"kestrel",
78 	"leprechaun",
79 	"medusa",
80 	"nymph",
81 	"orc",
82 	"phantom",
83 	"quagga",
84 	"rattlesnake",
85 	"snake",
86 	"troll",
87 	"black unicorn",
88 	"vampire",
89 	"wraith",
90 	"xeroc",
91 	"yeti",
92 	"zombie"
93 };
94 
95 object mon_tab[MONSTERS] = {
96 	{(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},
97 	{(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},
98 	{(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},
99 	{(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},
100 	{(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},
101 	{(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},
102 	{(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
103 			2000,20,126,85,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
104 	{(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},
105 	{(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},
106 	{(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},
107 	{(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},
108 	{(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},
109 	{(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
110 			250,18,126,85,0,25,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
111 	{(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},
112 	{(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},
113 	{(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},
114 	{(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},
115 	{(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},
116 	{(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},
117 	{(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},
118 	{(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
119 			200,17,26,85,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
120 	{(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
121 			350,19,126,85,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
122 	{(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},
123 	{(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},
124 	{(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},
125 	{(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}
126 };
127 
128 extern short cur_level;
129 extern short cur_room, party_room;
130 extern short blind, halluc, haste_self;
131 extern boolean detect_monster, see_invisible, r_see_invisible;
132 extern short stealthy;
133 
134 void
135 put_mons(void)
136 {
137 	short i;
138 	short n;
139 	object *monster;
140 	short row, col;
141 
142 	n = get_rand(4, 6);
143 
144 	for (i = 0; i < n; i++) {
145 		monster = gr_monster((object *) 0, 0);
146 		if ((monster->m_flags & WANDERS) && coin_toss()) {
147 			wake_up(monster);
148 		}
149 		gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
150 		put_m_at(row, col, monster);
151 	}
152 }
153 
154 object *
155 gr_monster(object *monster, int mn)
156 {
157 	if (!monster) {
158 		monster = alloc_object();
159 
160 		for (;;) {
161 			mn = get_rand(0, MONSTERS-1);
162 			if ((cur_level >= mon_tab[mn].first_level) &&
163 			(cur_level <= mon_tab[mn].last_level)) {
164 				break;
165 			}
166 		}
167 	}
168 	*monster = mon_tab[mn];
169 	if (monster->m_flags & IMITATES) {
170 		monster->disguise = gr_obj_char();
171 	}
172 	if (cur_level > (AMULET_LEVEL + 2)) {
173 		monster->m_flags |= HASTED;
174 	}
175 	monster->trow = NO_ROOM;
176 	return(monster);
177 }
178 
179 void
180 mv_mons(void)
181 {
182 	object *monster, *next_monster, *test_mons;
183 	boolean flew;
184 
185 	if (haste_self % 2) {
186 		return;
187 	}
188 
189 	monster = level_monsters.next_monster;
190 
191 	while (monster) {
192 		next_monster = monster->next_monster;
193 		mon_disappeared = 0;
194 		if (monster->m_flags & HASTED) {
195 			mv_1_monster(monster, rogue.row, rogue.col);
196 			if (mon_disappeared) {
197 				goto NM;
198 			}
199 		} else if (monster->m_flags & SLOWED) {
200 			monster->slowed_toggle = !monster->slowed_toggle;
201 			if (monster->slowed_toggle) {
202 				goto NM;
203 			}
204 		}
205 		if ((monster->m_flags & CONFUSED) && move_confused(monster)) {
206 			goto NM;
207 		}
208 		flew = 0;
209 		if (	(monster->m_flags & FLIES) &&
210 				!(monster->m_flags & NAPPING) &&
211 				!mon_can_go(monster, rogue.row, rogue.col)) {
212 			flew = 1;
213 			mv_1_monster(monster, rogue.row, rogue.col);
214 			if (mon_disappeared) {
215 				goto NM;
216 			}
217 		}
218 		if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) {
219 			mv_1_monster(monster, rogue.row, rogue.col);
220 		}
221 NM:		test_mons = level_monsters.next_monster;
222 		monster = NULL;
223 		while(test_mons)
224 		{
225 			if(next_monster == test_mons)
226 			{
227 				monster = next_monster;
228 				break;
229 			}
230 			test_mons = test_mons->next_monster;
231 		}
232 	}
233 }
234 
235 void
236 party_monsters(int rn, int n)
237 {
238 	short i, j;
239 	short row, col;
240 	object *monster;
241 	boolean found;
242 
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 short
276 gmc_row_col(int row, int col)
277 {
278 	object *monster;
279 
280 	if ((monster = object_at(&level_monsters, row, col))) {
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 short
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 boolean
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 boolean
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 short
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 	for (i = 0; ((i < 15) && (!found)); i++) {
624 		monster = gr_monster((object *) 0, 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 		message("you hear a faint cry of anguish in the distance", 0);
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 	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 	r = get_rand(0, 12);
723 
724 	for (i = 0; i < 4; i++) {
725 		d = (r + i) % 4;
726 		if (rooms[rn].doors[d].oth_room != NO_ROOM) {
727 			monster->trow = rooms[rn].doors[d].door_row;
728 			monster->tcol = rooms[rn].doors[d].door_col;
729 			break;
730 		}
731 	}
732 }
733 
734 int
735 rogue_can_see(int row, int col)
736 {
737 	int retval;
738 
739 	retval = !blind &&
740 			(((get_room_number(row, col) == cur_room) &&
741 					!(rooms[cur_room].is_room & R_MAZE)) ||
742 			rogue_is_around(row, col));
743 
744 	return(retval);
745 }
746 
747 static boolean
748 move_confused(object *monster)
749 {
750 	short i, row, col;
751 
752 	if (!(monster->m_flags & ASLEEP)) {
753 		if (--monster->moves_confused <= 0) {
754 			monster->m_flags &= (~CONFUSED);
755 		}
756 		if (monster->m_flags & STATIONARY) {
757 			return(coin_toss() ? 1 : 0);
758 		} else if (rand_percent(15)) {
759 			return(1);
760 		}
761 		row = monster->row;
762 		col = monster->col;
763 
764 		for (i = 0; i < 9; i++) {
765 			rand_around(i, &row, &col);
766 			if ((row == rogue.row) && (col == rogue.col)) {
767 				return(0);
768 			}
769 			if (mtry(monster, row, col)) {
770 				return(1);
771 			}
772 		}
773 	}
774 	return(0);
775 }
776 
777 static boolean
778 flit(object *monster)
779 {
780 	short i, row, col;
781 
782 	if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
783 		return(0);
784 	}
785 	if (rand_percent(10)) {
786 		return(1);
787 	}
788 	row = monster->row;
789 	col = monster->col;
790 
791 	for (i = 0; i < 9; i++) {
792 		rand_around(i, &row, &col);
793 		if ((row == rogue.row) && (col == rogue.col)) {
794 			continue;
795 		}
796 		if (mtry(monster, row, col)) {
797 			return(1);
798 		}
799 	}
800 	return(1);
801 }
802 
803 char
804 gr_obj_char(void)
805 {
806 	short r;
807 	const char *rs = "%!?]=/):*";
808 
809 	r = get_rand(0, 8);
810 
811 	return(rs[r]);
812 }
813 
814 static boolean
815 no_room_for_monster(int rn)
816 {
817 	short i, j;
818 
819 	for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
820 		for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
821 			if (!(dungeon[i][j] & MONSTER)) {
822 				return(0);
823 			}
824 		}
825 	}
826 	return(1);
827 }
828 
829 void
830 aggravate(void)
831 {
832 	object *monster;
833 
834 	message("you hear a high pitched humming noise", 0);
835 
836 	monster = level_monsters.next_monster;
837 
838 	while (monster) {
839 		wake_up(monster);
840 		monster->m_flags &= (~IMITATES);
841 		if (rogue_can_see(monster->row, monster->col)) {
842 			mvaddch(monster->row, monster->col, monster->m_char);
843 		}
844 		monster = monster->next_monster;
845 	}
846 }
847 
848 boolean
849 mon_sees(const object *monster, int row, int col)
850 {
851 	short rn, rdif, cdif, retval;
852 
853 	rn = get_room_number(row, col);
854 
855 	if (	(rn != NO_ROOM) &&
856 			(rn == get_room_number(monster->row, monster->col)) &&
857 			!(rooms[rn].is_room & R_MAZE)) {
858 		return(1);
859 	}
860 	rdif = row - monster->row;
861 	cdif = col - monster->col;
862 
863 	retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
864 	return(retval);
865 }
866 
867 void
868 mv_aquatars(void)
869 {
870 	object *monster;
871 
872 	monster = level_monsters.next_monster;
873 
874 	while (monster) {
875 		if ((monster->m_char == 'A') &&
876 			mon_can_go(monster, rogue.row, rogue.col)) {
877 			mv_1_monster(monster, rogue.row, rogue.col);
878 			monster->m_flags |= ALREADY_MOVED;
879 		}
880 		monster = monster->next_monster;
881 	}
882 }
883