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