Generally use NULL instead of explicitly casting 0 to some pointer type (part2).
[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.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(NULL, 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(NULL, 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(NULL, 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(NULL, 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 }