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