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