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