Initial import from FreeBSD RELENG_4:
[dragonfly.git] / games / rogue / monster.c
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
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)monster.c   8.1 (Berkeley) 5/31/93";
40 #endif
41 static const char rcsid[] =
42  "$FreeBSD: src/games/rogue/monster.c,v 1.6 1999/11/30 03:49:24 billf Exp $";
43 #endif /* not lint */
44
45 /*
46  * monster.c
47  *
48  * This source herein may be modified and/or distributed by anybody who
49  * so desires, with the following restrictions:
50  *    1.)  No portion of this notice shall be removed.
51  *    2.)  Credit shall not be taken for the creation of this source.
52  *    3.)  This code is not to be traded, sold, or used for personal
53  *         gain or profit.
54  *
55  */
56
57 #include "rogue.h"
58
59 object level_monsters;
60 boolean mon_disappeared;
61
62 const char *const m_names[] = {
63         "aquator",
64         "bat",
65         "centaur",
66         "dragon",
67         "emu",
68         "venus fly-trap",
69         "griffin",
70         "hobgoblin",
71         "ice monster",
72         "jabberwock",
73         "kestrel",
74         "leprechaun",
75         "medusa",
76         "nymph",
77         "orc",
78         "phantom",
79         "quagga",
80         "rattlesnake",
81         "snake",
82         "troll",
83         "black unicorn",
84         "vampire",
85         "wraith",
86         "xeroc",
87         "yeti",
88         "zombie"
89 };
90
91 object mon_tab[MONSTERS] = {
92         {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0,0,0,0},
93         {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0,0,0,0},
94         {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10,0,0,0},
95         {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90,0,0,0},
96         {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0,0,0,0},
97         {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0,0,0,0},
98         {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
99                         2000,20,126,85,0,10,0,0,0},
100         {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0,0,0,0},
101         {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0,0,0,0},
102         {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0,0,0,0},
103         {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0,0,0,0},
104         {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0,0,0,0},
105         {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
106                         250,18,126,85,0,25,0,0,0},
107         {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100,0,0,0},
108         {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10,0,0,0},
109         {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50,0,0,0},
110         {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20,0,0,0},
111         {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0,0,0,0},
112         {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0,0,0,0},
113         {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33,0,0,0},
114         {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
115                         200,17,26,85,0,33,0,0,0},
116         {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
117                         350,19,126,85,0,18,0,0,0},
118         {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0,0,0,0},
119         {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0,0,0,0},
120         {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20,0,0,0},
121         {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0,0,0,0}
122 };
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 put_mons()
131 {
132         short i;
133         short n;
134         object *monster;
135         short row, col;
136
137         n = get_rand(4, 6);
138
139         for (i = 0; i < n; i++) {
140                 monster = gr_monster((object *) 0, 0);
141                 if ((monster->m_flags & WANDERS) && coin_toss()) {
142                         wake_up(monster);
143                 }
144                 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
145                 put_m_at(row, col, monster);
146         }
147 }
148
149 object *
150 gr_monster(monster, mn)
151 object *monster;
152 int mn;
153 {
154         if (!monster) {
155                 monster = alloc_object();
156
157                 for (;;) {
158                         mn = get_rand(0, MONSTERS-1);
159                         if ((cur_level >= mon_tab[mn].first_level) &&
160                         (cur_level <= mon_tab[mn].last_level)) {
161                                 break;
162                         }
163                 }
164         }
165         *monster = mon_tab[mn];
166         if (monster->m_flags & IMITATES) {
167                 monster->disguise = gr_obj_char();
168         }
169         if (cur_level > (AMULET_LEVEL + 2)) {
170                 monster->m_flags |= HASTED;
171         }
172         monster->trow = NO_ROOM;
173         return(monster);
174 }
175
176 mv_mons()
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 party_monsters(rn, n)
232 int rn, 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((object *) 0, 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 gmc_row_col(row, col)
272 int row, col;
273 {
274         object *monster;
275
276         if (monster = object_at(&level_monsters, row, col)) {
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 gmc(monster)
291 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 mv_1_monster(monster, row, col)
305 object *monster;
306 short row, col;
307 {
308         short i, n;
309         boolean tried[6];
310
311         if (monster->m_flags & ASLEEP) {
312                 if (monster->m_flags & NAPPING) {
313                         if (--monster->nap_length <= 0) {
314                                 monster->m_flags &= (~(NAPPING | ASLEEP));
315                         }
316                         return;
317                 }
318                 if ((monster->m_flags & WAKENS) &&
319                          rogue_is_around(monster->row, monster->col) &&
320                          rand_percent(((stealthy > 0) ?
321                                 (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) :
322                                 WAKE_PERCENT))) {
323                         wake_up(monster);
324                 }
325                 return;
326         } else if (monster->m_flags & ALREADY_MOVED) {
327                 monster->m_flags &= (~ALREADY_MOVED);
328                 return;
329         }
330         if ((monster->m_flags & FLITS) && flit(monster)) {
331                 return;
332         }
333         if ((monster->m_flags & STATIONARY) &&
334                 (!mon_can_go(monster, rogue.row, rogue.col))) {
335                 return;
336         }
337         if (monster->m_flags & FREEZING_ROGUE) {
338                 return;
339         }
340         if ((monster->m_flags & CONFUSES) && m_confuse(monster)) {
341                 return;
342         }
343         if (mon_can_go(monster, rogue.row, rogue.col)) {
344                 mon_hit(monster);
345                 return;
346         }
347         if ((monster->m_flags & FLAMES) && flame_broil(monster)) {
348                 return;
349         }
350         if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) {
351                 return;
352         }
353         if ((monster->trow == monster->row) &&
354                    (monster->tcol == monster->col)) {
355                 monster->trow = NO_ROOM;
356         } else if (monster->trow != NO_ROOM) {
357                 row = monster->trow;
358                 col = monster->tcol;
359         }
360         if (monster->row > row) {
361                 row = monster->row - 1;
362         } else if (monster->row < row) {
363                 row = monster->row + 1;
364         }
365         if ((dungeon[row][monster->col] & DOOR) &&
366                  mtry(monster, row, monster->col)) {
367                 return;
368         }
369         if (monster->col > col) {
370                 col = monster->col - 1;
371         } else if (monster->col < col) {
372                 col = monster->col + 1;
373         }
374         if ((dungeon[monster->row][col] & DOOR) &&
375                  mtry(monster, monster->row, col)) {
376                 return;
377         }
378         if (mtry(monster, row, col)) {
379                 return;
380         }
381
382         for (i = 0; i <= 5; i++) tried[i] = 0;
383
384         for (i = 0; i < 6; i++) {
385 NEXT_TRY:       n = get_rand(0, 5);
386                 switch(n) {
387                 case 0:
388                         if (!tried[n] && mtry(monster, row, monster->col-1)) {
389                                 goto O;
390                         }
391                         break;
392                 case 1:
393                         if (!tried[n] && mtry(monster, row, monster->col)) {
394                                 goto O;
395                         }
396                         break;
397                 case 2:
398                         if (!tried[n] && mtry(monster, row, monster->col+1)) {
399                                 goto O;
400                         }
401                         break;
402                 case 3:
403                         if (!tried[n] && mtry(monster, monster->row-1, col)) {
404                                 goto O;
405                         }
406                         break;
407                 case 4:
408                         if (!tried[n] && mtry(monster, monster->row, col)) {
409                                 goto O;
410                         }
411                         break;
412                 case 5:
413                         if (!tried[n] && mtry(monster, monster->row+1, col)) {
414                                 goto O;
415                         }
416                         break;
417                 }
418                 if (!tried[n]) {
419                         tried[n] = 1;
420                 } else {
421                         goto NEXT_TRY;
422                 }
423         }
424 O:
425         if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) {
426                 if (++(monster->o) > 4) {
427                         if ((monster->trow == NO_ROOM) &&
428                                         (!mon_sees(monster, rogue.row, rogue.col))) {
429                                 monster->trow = get_rand(1, (DROWS - 2));
430                                 monster->tcol = get_rand(0, (DCOLS - 1));
431                         } else {
432                                 monster->trow = NO_ROOM;
433                                 monster->o = 0;
434                         }
435                 }
436         } else {
437                 monster->o_row = monster->row;
438                 monster->o_col = monster->col;
439                 monster->o = 0;
440         }
441 }
442
443 mtry(monster, row, col)
444 object *monster;
445 short row, col;
446 {
447         if (mon_can_go(monster, row, col)) {
448                 move_mon_to(monster, row, col);
449                 return(1);
450         }
451         return(0);
452 }
453
454 move_mon_to(monster, row, col)
455 object *monster;
456 short row, col;
457 {
458         short c;
459         int mrow, mcol;
460
461         mrow = monster->row;
462         mcol = monster->col;
463
464         dungeon[mrow][mcol] &= ~MONSTER;
465         dungeon[row][col] |= MONSTER;
466
467         c = mvinch(mrow, mcol);
468
469         if ((c >= 'A') && (c <= 'Z')) {
470                 if (!detect_monster) {
471                         mvaddch(mrow, mcol, monster->trail_char);
472                 } else {
473                         if (rogue_can_see(mrow, mcol)) {
474                                 mvaddch(mrow, mcol, monster->trail_char);
475                         } else {
476                                 if (monster->trail_char == '.') {
477                                         monster->trail_char = ' ';
478                                 }
479                                 mvaddch(mrow, mcol, monster->trail_char);
480                         }
481                 }
482         }
483         monster->trail_char = mvinch(row, col);
484         if (!blind && (detect_monster || rogue_can_see(row, col))) {
485                 if ((!(monster->m_flags & INVISIBLE) ||
486                         (detect_monster || see_invisible || r_see_invisible))) {
487                         mvaddch(row, col, gmc(monster));
488                 }
489         }
490         if ((dungeon[row][col] & DOOR) &&
491                 (get_room_number(row, col) != cur_room) &&
492                 (dungeon[mrow][mcol] == FLOOR) && !blind) {
493                         mvaddch(mrow, mcol, ' ');
494         }
495         if (dungeon[row][col] & DOOR) {
496                         dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0),
497                                 row, col);
498         } else {
499                 monster->row = row;
500                 monster->col = col;
501         }
502 }
503
504 mon_can_go(monster, row, col)
505 const object *monster;
506 short row, 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 wake_up(monster)
546 object *monster;
547 {
548         if (!(monster->m_flags & NAPPING)) {
549                 monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
550         }
551 }
552
553 wake_room(rn, entering, row, col)
554 short rn;
555 boolean entering;
556 short row, col;
557 {
558         object *monster;
559         short wake_percent;
560         boolean in_room;
561
562         wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT;
563         if (stealthy > 0) {
564                 wake_percent /= (STEALTH_FACTOR + stealthy);
565         }
566
567         monster = level_monsters.next_monster;
568
569         while (monster) {
570                 in_room = (rn == get_room_number(monster->row, monster->col));
571                 if (in_room) {
572                         if (entering) {
573                                 monster->trow = NO_ROOM;
574                         } else {
575                                 monster->trow = row;
576                                 monster->tcol = col;
577                         }
578                 }
579                 if ((monster->m_flags & WAKENS) &&
580                         (rn == get_room_number(monster->row, monster->col))) {
581                         if (rand_percent(wake_percent)) {
582                                 wake_up(monster);
583                         }
584                 }
585                 monster = monster->next_monster;
586         }
587 }
588
589 const char *
590 mon_name(monster)
591 const object *monster;
592 {
593         short ch;
594
595         if (blind || ((monster->m_flags & INVISIBLE) &&
596                 !(detect_monster || see_invisible || r_see_invisible))) {
597                 return("something");
598         }
599         if (halluc) {
600                 ch = get_rand('A', 'Z') - 'A';
601                 return(m_names[ch]);
602         }
603         ch = monster->m_char - 'A';
604         return(m_names[ch]);
605 }
606
607 rogue_is_around(row, col)
608 int row, col;
609 {
610         short rdif, cdif, retval;
611
612         rdif = row - rogue.row;
613         cdif = col - rogue.col;
614
615         retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
616         return(retval);
617 }
618
619 wanderer()
620 {
621         object *monster;
622         short row, col, i;
623         boolean found = 0;
624
625         for (i = 0; ((i < 15) && (!found)); i++) {
626                 monster = gr_monster((object *) 0, 0);
627                 if (!(monster->m_flags & (WAKENS | WANDERS))) {
628                         free_object(monster);
629                 } else {
630                         found = 1;
631                 }
632         }
633         if (found) {
634                 found = 0;
635                 wake_up(monster);
636                 for (i = 0; ((i < 25) && (!found)); i++) {
637                         gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
638                         if (!rogue_can_see(row, col)) {
639                                 put_m_at(row, col, monster);
640                                 found = 1;
641                         }
642                 }
643                 if (!found) {
644                         free_object(monster);
645                 }
646         }
647 }
648
649 show_monsters()
650 {
651         object *monster;
652
653         detect_monster = 1;
654
655         if (blind) {
656                 return;
657         }
658         monster = level_monsters.next_monster;
659
660         while (monster) {
661                 mvaddch(monster->row, monster->col, monster->m_char);
662                 if (monster->m_flags & IMITATES) {
663                         monster->m_flags &= (~IMITATES);
664                         monster->m_flags |= WAKENS;
665                 }
666                 monster = monster->next_monster;
667         }
668 }
669
670 create_monster()
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 put_m_at(row, col, monster)
706 short row, col;
707 object *monster;
708 {
709         monster->row = row;
710         monster->col = col;
711         dungeon[row][col] |= MONSTER;
712         monster->trail_char = mvinch(row, col);
713         (void) add_to_pack(monster, &level_monsters, 0);
714         aim_monster(monster);
715 }
716
717 aim_monster(monster)
718 object *monster;
719 {
720         short i, rn, d, r;
721
722         rn = get_room_number(monster->row, monster->col);
723         r = get_rand(0, 12);
724
725         for (i = 0; i < 4; i++) {
726                 d = (r + i) % 4;
727                 if (rooms[rn].doors[d].oth_room != NO_ROOM) {
728                         monster->trow = rooms[rn].doors[d].door_row;
729                         monster->tcol = rooms[rn].doors[d].door_col;
730                         break;
731                 }
732         }
733 }
734
735 rogue_can_see(row, col)
736 int row, col;
737 {
738         int retval;
739
740         retval = !blind &&
741                         (((get_room_number(row, col) == cur_room) &&
742                                         !(rooms[cur_room].is_room & R_MAZE)) ||
743                         rogue_is_around(row, col));
744
745         return(retval);
746 }
747
748 move_confused(monster)
749 object *monster;
750 {
751         short i, row, col;
752
753         if (!(monster->m_flags & ASLEEP)) {
754                 if (--monster->moves_confused <= 0) {
755                         monster->m_flags &= (~CONFUSED);
756                 }
757                 if (monster->m_flags & STATIONARY) {
758                         return(coin_toss() ? 1 : 0);
759                 } else if (rand_percent(15)) {
760                         return(1);
761                 }
762                 row = monster->row;
763                 col = monster->col;
764
765                 for (i = 0; i < 9; i++) {
766                         rand_around(i, &row, &col);
767                         if ((row == rogue.row) && (col == rogue.col)) {
768                                 return(0);
769                         }
770                         if (mtry(monster, row, col)) {
771                                 return(1);
772                         }
773                 }
774         }
775         return(0);
776 }
777
778 flit(monster)
779 object *monster;
780 {
781         short i, row, col;
782
783         if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
784                 return(0);
785         }
786         if (rand_percent(10)) {
787                 return(1);
788         }
789         row = monster->row;
790         col = monster->col;
791
792         for (i = 0; i < 9; i++) {
793                 rand_around(i, &row, &col);
794                 if ((row == rogue.row) && (col == rogue.col)) {
795                         continue;
796                 }
797                 if (mtry(monster, row, col)) {
798                         return(1);
799                 }
800         }
801         return(1);
802 }
803
804 gr_obj_char()
805 {
806         short r;
807         const char *rs = "%!?]=/):*";
808
809         r = get_rand(0, 8);
810
811         return(rs[r]);
812 }
813
814 no_room_for_monster(rn)
815 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 aggravate()
830 {
831         object *monster;
832
833         message("you hear a high pitched humming noise", 0);
834
835         monster = level_monsters.next_monster;
836
837         while (monster) {
838                 wake_up(monster);
839                 monster->m_flags &= (~IMITATES);
840                 if (rogue_can_see(monster->row, monster->col)) {
841                         mvaddch(monster->row, monster->col, monster->m_char);
842                 }
843                 monster = monster->next_monster;
844         }
845 }
846
847 boolean
848 mon_sees(monster, row, col)
849 const object *monster;
850 int row, col;
851 {
852         short rn, rdif, cdif, retval;
853
854         rn = get_room_number(row, col);
855
856         if (    (rn != NO_ROOM) &&
857                         (rn == get_room_number(monster->row, monster->col)) &&
858                         !(rooms[rn].is_room & R_MAZE)) {
859                 return(1);
860         }
861         rdif = row - monster->row;
862         cdif = col - monster->col;
863
864         retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
865         return(retval);
866 }
867
868 mv_aquatars()
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 }