xref: /dragonfly/games/rogue/monster.c (revision 3536f98c1c75c45bd29f481cf18b0f90cea395f7)
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
put_mons(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 *
gr_monster(object * monster,int mn)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
mv_mons(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
party_monsters(int rn,int n)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
gmc_row_col(int row,int col)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
gmc(object * monster)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
mv_1_monster(object * monster,short row,short col)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
mtry(object * monster,short row,short col)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
move_mon_to(object * monster,short row,short col)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
mon_can_go(const object * monster,short row,short col)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
wake_up(object * monster)536 wake_up(object *monster)
537 {
538           if (!(monster->m_flags & NAPPING)) {
539                     monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
540           }
541 }
542 
543 void
wake_room(short rn,boolean entering,short row,short col)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 *
mon_name(const object * monster)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
rogue_is_around(int row,int col)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
wanderer(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
show_monsters(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
create_monster(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
put_m_at(short row,short col,object * monster)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
aim_monster(object * monster)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
rogue_can_see(int row,int col)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
move_confused(object * monster)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
flit(object * monster)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
gr_obj_char(void)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
no_room_for_monster(int rn)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
aggravate(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
mon_sees(const object * monster,int row,int col)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
mv_aquatars(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 }
875