52958bdc2e470f153c50b66a0e5d2a25b65f7f05
[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  * @(#)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.2 2003/06/17 04:25:24 dillon 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 object level_monsters;
56 boolean mon_disappeared;
57
58 const char *const m_names[] = {
59         "aquator",
60         "bat",
61         "centaur",
62         "dragon",
63         "emu",
64         "venus fly-trap",
65         "griffin",
66         "hobgoblin",
67         "ice monster",
68         "jabberwock",
69         "kestrel",
70         "leprechaun",
71         "medusa",
72         "nymph",
73         "orc",
74         "phantom",
75         "quagga",
76         "rattlesnake",
77         "snake",
78         "troll",
79         "black unicorn",
80         "vampire",
81         "wraith",
82         "xeroc",
83         "yeti",
84         "zombie"
85 };
86
87 object mon_tab[MONSTERS] = {
88         {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0,0,0,0},
89         {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0,0,0,0},
90         {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10,0,0,0},
91         {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90,0,0,0},
92         {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0,0,0,0},
93         {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0,0,0,0},
94         {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
95                         2000,20,126,85,0,10,0,0,0},
96         {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0,0,0,0},
97         {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0,0,0,0},
98         {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0,0,0,0},
99         {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0,0,0,0},
100         {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0,0,0,0},
101         {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
102                         250,18,126,85,0,25,0,0,0},
103         {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100,0,0,0},
104         {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10,0,0,0},
105         {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50,0,0,0},
106         {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20,0,0,0},
107         {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0,0,0,0},
108         {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0,0,0,0},
109         {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33,0,0,0},
110         {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
111                         200,17,26,85,0,33,0,0,0},
112         {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
113                         350,19,126,85,0,18,0,0,0},
114         {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0,0,0,0},
115         {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0,0,0,0},
116         {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20,0,0,0},
117         {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0,0,0,0}
118 };
119
120 extern short cur_level;
121 extern short cur_room, party_room;
122 extern short blind, halluc, haste_self;
123 extern boolean detect_monster, see_invisible, r_see_invisible;
124 extern short stealthy;
125
126 put_mons()
127 {
128         short i;
129         short n;
130         object *monster;
131         short row, col;
132
133         n = get_rand(4, 6);
134
135         for (i = 0; i < n; i++) {
136                 monster = gr_monster((object *) 0, 0);
137                 if ((monster->m_flags & WANDERS) && coin_toss()) {
138                         wake_up(monster);
139                 }
140                 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
141                 put_m_at(row, col, monster);
142         }
143 }
144
145 object *
146 gr_monster(monster, mn)
147 object *monster;
148 int mn;
149 {
150         if (!monster) {
151                 monster = alloc_object();
152
153                 for (;;) {
154                         mn = get_rand(0, MONSTERS-1);
155                         if ((cur_level >= mon_tab[mn].first_level) &&
156                         (cur_level <= mon_tab[mn].last_level)) {
157                                 break;
158                         }
159                 }
160         }
161         *monster = mon_tab[mn];
162         if (monster->m_flags & IMITATES) {
163                 monster->disguise = gr_obj_char();
164         }
165         if (cur_level > (AMULET_LEVEL + 2)) {
166                 monster->m_flags |= HASTED;
167         }
168         monster->trow = NO_ROOM;
169         return(monster);
170 }
171
172 mv_mons()
173 {
174         object *monster, *next_monster, *test_mons;
175         boolean flew;
176
177         if (haste_self % 2) {
178                 return;
179         }
180
181         monster = level_monsters.next_monster;
182
183         while (monster) {
184                 next_monster = monster->next_monster;
185                 mon_disappeared = 0;
186                 if (monster->m_flags & HASTED) {
187                         mv_1_monster(monster, rogue.row, rogue.col);
188                         if (mon_disappeared) {
189                                 goto NM;
190                         }
191                 } else if (monster->m_flags & SLOWED) {
192                         monster->slowed_toggle = !monster->slowed_toggle;
193                         if (monster->slowed_toggle) {
194                                 goto NM;
195                         }
196                 }
197                 if ((monster->m_flags & CONFUSED) && move_confused(monster)) {
198                         goto NM;
199                 }
200                 flew = 0;
201                 if (    (monster->m_flags & FLIES) &&
202                                 !(monster->m_flags & NAPPING) &&
203                                 !mon_can_go(monster, rogue.row, rogue.col)) {
204                         flew = 1;
205                         mv_1_monster(monster, rogue.row, rogue.col);
206                         if (mon_disappeared) {
207                                 goto NM;
208                         }
209                 }
210                 if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) {
211                         mv_1_monster(monster, rogue.row, rogue.col);
212                 }
213 NM:             test_mons = level_monsters.next_monster;
214                 monster = NULL;
215                 while(test_mons)
216                 {
217                         if(next_monster == test_mons)
218                         {
219                                 monster = next_monster;
220                                 break;
221                         }
222                         test_mons = test_mons->next_monster;
223                 }
224         }
225 }
226
227 party_monsters(rn, n)
228 int rn, n;
229 {
230         short i, j;
231         short row, col;
232         object *monster;
233         boolean found;
234
235         n += n;
236
237         for (i = 0; i < MONSTERS; i++) {
238                 mon_tab[i].first_level -= (cur_level % 3);
239         }
240         for (i = 0; i < n; i++) {
241                 if (no_room_for_monster(rn)) {
242                         break;
243                 }
244                 for (j = found = 0; ((!found) && (j < 250)); j++) {
245                         row = get_rand(rooms[rn].top_row+1,
246                                 rooms[rn].bottom_row-1);
247                         col = get_rand(rooms[rn].left_col+1,
248                                 rooms[rn].right_col-1);
249                         if ((!(dungeon[row][col] & MONSTER)) &&
250                                 (dungeon[row][col] & (FLOOR | TUNNEL))) {
251                                 found = 1;
252                         }
253                 }
254                 if (found) {
255                         monster = gr_monster((object *) 0, 0);
256                         if (!(monster->m_flags & IMITATES)) {
257                                 monster->m_flags |= WAKENS;
258                         }
259                         put_m_at(row, col, monster);
260                 }
261         }
262         for (i = 0; i < MONSTERS; i++) {
263                 mon_tab[i].first_level += (cur_level % 3);
264         }
265 }
266
267 gmc_row_col(row, col)
268 int row, col;
269 {
270         object *monster;
271
272         if (monster = object_at(&level_monsters, row, col)) {
273                 if ((!(detect_monster || see_invisible || r_see_invisible) &&
274                         (monster->m_flags & INVISIBLE)) || blind) {
275                         return(monster->trail_char);
276                 }
277                 if (monster->m_flags & IMITATES) {
278                         return(monster->disguise);
279                 }
280                 return(monster->m_char);
281         } else {
282                 return('&');    /* BUG if this ever happens */
283         }
284 }
285
286 gmc(monster)
287 object *monster;
288 {
289         if ((!(detect_monster || see_invisible || r_see_invisible) &&
290                 (monster->m_flags & INVISIBLE))
291                 || blind) {
292                 return(monster->trail_char);
293         }
294         if (monster->m_flags & IMITATES) {
295                 return(monster->disguise);
296         }
297         return(monster->m_char);
298 }
299
300 mv_1_monster(monster, row, col)
301 object *monster;
302 short row, col;
303 {
304         short i, n;
305         boolean tried[6];
306
307         if (monster->m_flags & ASLEEP) {
308                 if (monster->m_flags & NAPPING) {
309                         if (--monster->nap_length <= 0) {
310                                 monster->m_flags &= (~(NAPPING | ASLEEP));
311                         }
312                         return;
313                 }
314                 if ((monster->m_flags & WAKENS) &&
315                          rogue_is_around(monster->row, monster->col) &&
316                          rand_percent(((stealthy > 0) ?
317                                 (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) :
318                                 WAKE_PERCENT))) {
319                         wake_up(monster);
320                 }
321                 return;
322         } else if (monster->m_flags & ALREADY_MOVED) {
323                 monster->m_flags &= (~ALREADY_MOVED);
324                 return;
325         }
326         if ((monster->m_flags & FLITS) && flit(monster)) {
327                 return;
328         }
329         if ((monster->m_flags & STATIONARY) &&
330                 (!mon_can_go(monster, rogue.row, rogue.col))) {
331                 return;
332         }
333         if (monster->m_flags & FREEZING_ROGUE) {
334                 return;
335         }
336         if ((monster->m_flags & CONFUSES) && m_confuse(monster)) {
337                 return;
338         }
339         if (mon_can_go(monster, rogue.row, rogue.col)) {
340                 mon_hit(monster);
341                 return;
342         }
343         if ((monster->m_flags & FLAMES) && flame_broil(monster)) {
344                 return;
345         }
346         if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) {
347                 return;
348         }
349         if ((monster->trow == monster->row) &&
350                    (monster->tcol == monster->col)) {
351                 monster->trow = NO_ROOM;
352         } else if (monster->trow != NO_ROOM) {
353                 row = monster->trow;
354                 col = monster->tcol;
355         }
356         if (monster->row > row) {
357                 row = monster->row - 1;
358         } else if (monster->row < row) {
359                 row = monster->row + 1;
360         }
361         if ((dungeon[row][monster->col] & DOOR) &&
362                  mtry(monster, row, monster->col)) {
363                 return;
364         }
365         if (monster->col > col) {
366                 col = monster->col - 1;
367         } else if (monster->col < col) {
368                 col = monster->col + 1;
369         }
370         if ((dungeon[monster->row][col] & DOOR) &&
371                  mtry(monster, monster->row, col)) {
372                 return;
373         }
374         if (mtry(monster, row, col)) {
375                 return;
376         }
377
378         for (i = 0; i <= 5; i++) tried[i] = 0;
379
380         for (i = 0; i < 6; i++) {
381 NEXT_TRY:       n = get_rand(0, 5);
382                 switch(n) {
383                 case 0:
384                         if (!tried[n] && mtry(monster, row, monster->col-1)) {
385                                 goto O;
386                         }
387                         break;
388                 case 1:
389                         if (!tried[n] && mtry(monster, row, monster->col)) {
390                                 goto O;
391                         }
392                         break;
393                 case 2:
394                         if (!tried[n] && mtry(monster, row, monster->col+1)) {
395                                 goto O;
396                         }
397                         break;
398                 case 3:
399                         if (!tried[n] && mtry(monster, monster->row-1, col)) {
400                                 goto O;
401                         }
402                         break;
403                 case 4:
404                         if (!tried[n] && mtry(monster, monster->row, col)) {
405                                 goto O;
406                         }
407                         break;
408                 case 5:
409                         if (!tried[n] && mtry(monster, monster->row+1, col)) {
410                                 goto O;
411                         }
412                         break;
413                 }
414                 if (!tried[n]) {
415                         tried[n] = 1;
416                 } else {
417                         goto NEXT_TRY;
418                 }
419         }
420 O:
421         if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) {
422                 if (++(monster->o) > 4) {
423                         if ((monster->trow == NO_ROOM) &&
424                                         (!mon_sees(monster, rogue.row, rogue.col))) {
425                                 monster->trow = get_rand(1, (DROWS - 2));
426                                 monster->tcol = get_rand(0, (DCOLS - 1));
427                         } else {
428                                 monster->trow = NO_ROOM;
429                                 monster->o = 0;
430                         }
431                 }
432         } else {
433                 monster->o_row = monster->row;
434                 monster->o_col = monster->col;
435                 monster->o = 0;
436         }
437 }
438
439 mtry(monster, row, col)
440 object *monster;
441 short row, col;
442 {
443         if (mon_can_go(monster, row, col)) {
444                 move_mon_to(monster, row, col);
445                 return(1);
446         }
447         return(0);
448 }
449
450 move_mon_to(monster, row, col)
451 object *monster;
452 short row, col;
453 {
454         short c;
455         int mrow, mcol;
456
457         mrow = monster->row;
458         mcol = monster->col;
459
460         dungeon[mrow][mcol] &= ~MONSTER;
461         dungeon[row][col] |= MONSTER;
462
463         c = mvinch(mrow, mcol);
464
465         if ((c >= 'A') && (c <= 'Z')) {
466                 if (!detect_monster) {
467                         mvaddch(mrow, mcol, monster->trail_char);
468                 } else {
469                         if (rogue_can_see(mrow, mcol)) {
470                                 mvaddch(mrow, mcol, monster->trail_char);
471                         } else {
472                                 if (monster->trail_char == '.') {
473                                         monster->trail_char = ' ';
474                                 }
475                                 mvaddch(mrow, mcol, monster->trail_char);
476                         }
477                 }
478         }
479         monster->trail_char = mvinch(row, col);
480         if (!blind && (detect_monster || rogue_can_see(row, col))) {
481                 if ((!(monster->m_flags & INVISIBLE) ||
482                         (detect_monster || see_invisible || r_see_invisible))) {
483                         mvaddch(row, col, gmc(monster));
484                 }
485         }
486         if ((dungeon[row][col] & DOOR) &&
487                 (get_room_number(row, col) != cur_room) &&
488                 (dungeon[mrow][mcol] == FLOOR) && !blind) {
489                         mvaddch(mrow, mcol, ' ');
490         }
491         if (dungeon[row][col] & DOOR) {
492                         dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0),
493                                 row, col);
494         } else {
495                 monster->row = row;
496                 monster->col = col;
497         }
498 }
499
500 mon_can_go(monster, row, col)
501 const object *monster;
502 short row, 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 wake_up(monster)
542 object *monster;
543 {
544         if (!(monster->m_flags & NAPPING)) {
545                 monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
546         }
547 }
548
549 wake_room(rn, entering, row, col)
550 short rn;
551 boolean entering;
552 short row, col;
553 {
554         object *monster;
555         short wake_percent;
556         boolean in_room;
557
558         wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT;
559         if (stealthy > 0) {
560                 wake_percent /= (STEALTH_FACTOR + stealthy);
561         }
562
563         monster = level_monsters.next_monster;
564
565         while (monster) {
566                 in_room = (rn == get_room_number(monster->row, monster->col));
567                 if (in_room) {
568                         if (entering) {
569                                 monster->trow = NO_ROOM;
570                         } else {
571                                 monster->trow = row;
572                                 monster->tcol = col;
573                         }
574                 }
575                 if ((monster->m_flags & WAKENS) &&
576                         (rn == get_room_number(monster->row, monster->col))) {
577                         if (rand_percent(wake_percent)) {
578                                 wake_up(monster);
579                         }
580                 }
581                 monster = monster->next_monster;
582         }
583 }
584
585 const char *
586 mon_name(monster)
587 const object *monster;
588 {
589         short ch;
590
591         if (blind || ((monster->m_flags & INVISIBLE) &&
592                 !(detect_monster || see_invisible || r_see_invisible))) {
593                 return("something");
594         }
595         if (halluc) {
596                 ch = get_rand('A', 'Z') - 'A';
597                 return(m_names[ch]);
598         }
599         ch = monster->m_char - 'A';
600         return(m_names[ch]);
601 }
602
603 rogue_is_around(row, col)
604 int row, col;
605 {
606         short rdif, cdif, retval;
607
608         rdif = row - rogue.row;
609         cdif = col - rogue.col;
610
611         retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
612         return(retval);
613 }
614
615 wanderer()
616 {
617         object *monster;
618         short row, col, i;
619         boolean found = 0;
620
621         for (i = 0; ((i < 15) && (!found)); i++) {
622                 monster = gr_monster((object *) 0, 0);
623                 if (!(monster->m_flags & (WAKENS | WANDERS))) {
624                         free_object(monster);
625                 } else {
626                         found = 1;
627                 }
628         }
629         if (found) {
630                 found = 0;
631                 wake_up(monster);
632                 for (i = 0; ((i < 25) && (!found)); i++) {
633                         gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
634                         if (!rogue_can_see(row, col)) {
635                                 put_m_at(row, col, monster);
636                                 found = 1;
637                         }
638                 }
639                 if (!found) {
640                         free_object(monster);
641                 }
642         }
643 }
644
645 show_monsters()
646 {
647         object *monster;
648
649         detect_monster = 1;
650
651         if (blind) {
652                 return;
653         }
654         monster = level_monsters.next_monster;
655
656         while (monster) {
657                 mvaddch(monster->row, monster->col, monster->m_char);
658                 if (monster->m_flags & IMITATES) {
659                         monster->m_flags &= (~IMITATES);
660                         monster->m_flags |= WAKENS;
661                 }
662                 monster = monster->next_monster;
663         }
664 }
665
666 create_monster()
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((object *) 0, 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 put_m_at(row, col, monster)
702 short row, col;
703 object *monster;
704 {
705         monster->row = row;
706         monster->col = col;
707         dungeon[row][col] |= MONSTER;
708         monster->trail_char = mvinch(row, col);
709         (void) add_to_pack(monster, &level_monsters, 0);
710         aim_monster(monster);
711 }
712
713 aim_monster(monster)
714 object *monster;
715 {
716         short i, rn, d, r;
717
718         rn = get_room_number(monster->row, monster->col);
719         r = get_rand(0, 12);
720
721         for (i = 0; i < 4; i++) {
722                 d = (r + i) % 4;
723                 if (rooms[rn].doors[d].oth_room != NO_ROOM) {
724                         monster->trow = rooms[rn].doors[d].door_row;
725                         monster->tcol = rooms[rn].doors[d].door_col;
726                         break;
727                 }
728         }
729 }
730
731 rogue_can_see(row, col)
732 int row, col;
733 {
734         int retval;
735
736         retval = !blind &&
737                         (((get_room_number(row, col) == cur_room) &&
738                                         !(rooms[cur_room].is_room & R_MAZE)) ||
739                         rogue_is_around(row, col));
740
741         return(retval);
742 }
743
744 move_confused(monster)
745 object *monster;
746 {
747         short i, row, col;
748
749         if (!(monster->m_flags & ASLEEP)) {
750                 if (--monster->moves_confused <= 0) {
751                         monster->m_flags &= (~CONFUSED);
752                 }
753                 if (monster->m_flags & STATIONARY) {
754                         return(coin_toss() ? 1 : 0);
755                 } else if (rand_percent(15)) {
756                         return(1);
757                 }
758                 row = monster->row;
759                 col = monster->col;
760
761                 for (i = 0; i < 9; i++) {
762                         rand_around(i, &row, &col);
763                         if ((row == rogue.row) && (col == rogue.col)) {
764                                 return(0);
765                         }
766                         if (mtry(monster, row, col)) {
767                                 return(1);
768                         }
769                 }
770         }
771         return(0);
772 }
773
774 flit(monster)
775 object *monster;
776 {
777         short i, row, col;
778
779         if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
780                 return(0);
781         }
782         if (rand_percent(10)) {
783                 return(1);
784         }
785         row = monster->row;
786         col = monster->col;
787
788         for (i = 0; i < 9; i++) {
789                 rand_around(i, &row, &col);
790                 if ((row == rogue.row) && (col == rogue.col)) {
791                         continue;
792                 }
793                 if (mtry(monster, row, col)) {
794                         return(1);
795                 }
796         }
797         return(1);
798 }
799
800 gr_obj_char()
801 {
802         short r;
803         const char *rs = "%!?]=/):*";
804
805         r = get_rand(0, 8);
806
807         return(rs[r]);
808 }
809
810 no_room_for_monster(rn)
811 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 aggravate()
826 {
827         object *monster;
828
829         message("you hear a high pitched humming noise", 0);
830
831         monster = level_monsters.next_monster;
832
833         while (monster) {
834                 wake_up(monster);
835                 monster->m_flags &= (~IMITATES);
836                 if (rogue_can_see(monster->row, monster->col)) {
837                         mvaddch(monster->row, monster->col, monster->m_char);
838                 }
839                 monster = monster->next_monster;
840         }
841 }
842
843 boolean
844 mon_sees(monster, row, col)
845 const object *monster;
846 int row, col;
847 {
848         short rn, rdif, cdif, retval;
849
850         rn = get_room_number(row, col);
851
852         if (    (rn != NO_ROOM) &&
853                         (rn == get_room_number(monster->row, monster->col)) &&
854                         !(rooms[rn].is_room & R_MAZE)) {
855                 return(1);
856         }
857         rdif = row - monster->row;
858         cdif = col - monster->col;
859
860         retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
861         return(retval);
862 }
863
864 mv_aquatars()
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 }