/*      $NetBSD: monster.c,v 1.18 2025/04/07 14:36:28 hgutch Exp $      */

/*
* Copyright (c) 1988, 1993
*      The Regents of the University of California.  All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Timothy C. Stoehr.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)monster.c   8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: monster.c,v 1.18 2025/04/07 14:36:28 hgutch Exp $");
#endif
#endif /* not lint */

/*
* monster.c
*
* This source herein may be modified and/or distributed by anybody who
* so desires, with the following restrictions:
*    1.)  No portion of this notice shall be removed.
*    2.)  Credit shall not be taken for the creation of this source.
*    3.)  This code is not to be traded, sold, or used for personal
*         gain or profit.
*
*/

#include "rogue.h"

object level_monsters;
boolean mon_disappeared;

const char *const m_names[] = {
       "aquator",
       "bat",
       "centaur",
       "dragon",
       "emu",
       "venus fly-trap",
       "griffin",
       "hobgoblin",
       "ice monster",
       "jabberwock",
       "kestrel",
       "leprechaun",
       "medusa",
       "nymph",
       "orc",
       "phantom",
       "quagga",
       "rattlesnake",
       "snake",
       "troll",
       "black unicorn",
       "vampire",
       "wraith",
       "xeroc",
       "yeti",
       "zombie"
};

#define FILL 0,0,0,0,0,0,0,0,0,0,0,0,0,NULL

static object mon_tab[MONSTERS] = {
       {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0, FILL},
       {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0, FILL},
       {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10, FILL},
       {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90, FILL},
       {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0, FILL},
       {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0, FILL},
       {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
                       2000,20,126,85,0,10, FILL},
       {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0, FILL},
       {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0, FILL},
       {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0, FILL},
       {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0, FILL},
       {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0, FILL},
       {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
                       250,18,126,85,0,25, FILL},
       {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100, FILL},
       {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10, FILL},
       {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50, FILL},
       {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20, FILL},
       {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0, FILL},
       {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0, FILL},
       {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33, FILL},
       {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
                       200,17,26,85,0,33, FILL},
       {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
                       350,19,126,85,0,18, FILL},
       {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0, FILL},
       {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0, FILL},
       {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20, FILL},
       {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0, FILL}
};

static void aim_monster(object *);
static int flit(object *);
static int move_confused(object *);
static int mtry(object *, short, short);
static int no_room_for_monster(int);
static void put_m_at(short, short, object *);
static int rogue_is_around(int, int);

void
set_monster_damage(object *obj)
{
       for (short i = 0; i < MONSTERS; i++) {
               if (obj->ichar == mon_tab[i].ichar) {
                       obj->damage = mon_tab[i].damage;
                       break;
               }
       }
}

void
put_mons(void)
{
       short i;
       short n;
       object *monster;
       short row, col;

       n = get_rand(4, 6);

       for (i = 0; i < n; i++) {
               monster = gr_monster(NULL, 0);
               if ((monster->m_flags & WANDERS) && coin_toss()) {
                       wake_up(monster);
               }
               gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
               put_m_at(row, col, monster);
       }
}

object *
gr_monster(object *monster, int mn)
{
       if (!monster) {
               monster = alloc_object();

               for (;;) {
                       mn = get_rand(0, MONSTERS-1);
                       if ((cur_level >= mon_tab[mn].first_level) &&
                       (cur_level <= mon_tab[mn].last_level)) {
                               break;
                       }
               }
       }
       *monster = mon_tab[mn];
       if (monster->m_flags & IMITATES) {
               monster->disguise = gr_obj_char();
       }
       if (cur_level > (AMULET_LEVEL + 2)) {
               monster->m_flags |= HASTED;
       }
       monster->trow = NO_ROOM;
       return(monster);
}

void
mv_mons(void)
{
       object *monster, *next_monster, *test_mons;
       boolean flew;

       if (haste_self % 2) {
               return;
       }

       monster = level_monsters.next_monster;

       while (monster) {
               next_monster = monster->next_monster;
               mon_disappeared = 0;
               if (monster->m_flags & HASTED) {
                       mv_1_monster(monster, rogue.row, rogue.col);
                       if (mon_disappeared) {
                               goto NM;
                       }
               } else if (monster->m_flags & SLOWED) {
                       monster->slowed_toggle = !monster->slowed_toggle;
                       if (monster->slowed_toggle) {
                               goto NM;
                       }
               }
               if ((monster->m_flags & CONFUSED) && move_confused(monster)) {
                       goto NM;
               }
               flew = 0;
               if (    (monster->m_flags & FLIES) &&
                               !(monster->m_flags & NAPPING) &&
                               !mon_can_go(monster, rogue.row, rogue.col)) {
                       flew = 1;
                       mv_1_monster(monster, rogue.row, rogue.col);
                       if (mon_disappeared) {
                               goto NM;
                       }
               }
               if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) {
                       mv_1_monster(monster, rogue.row, rogue.col);
               }
NM:             test_mons = level_monsters.next_monster;
               monster = NULL;
               while (test_mons)
               {
                       if (next_monster == test_mons)
                       {
                               monster = next_monster;
                               break;
                       }
                       test_mons = test_mons -> next_monster;
               }
       }
}

void
party_monsters(int rn, int n)
{
       short i, j;
       short row, col;
       object *monster;
       boolean found;

       row = col = 0;
       n += n;

       for (i = 0; i < MONSTERS; i++) {
               mon_tab[i].first_level -= (cur_level % 3);
       }
       for (i = 0; i < n; i++) {
               if (no_room_for_monster(rn)) {
                       break;
               }
               for (j = found = 0; ((!found) && (j < 250)); j++) {
                       row = get_rand(rooms[rn].top_row+1,
                               rooms[rn].bottom_row-1);
                       col = get_rand(rooms[rn].left_col+1,
                               rooms[rn].right_col-1);
                       if ((!(dungeon[row][col] & MONSTER)) &&
                               (dungeon[row][col] & (FLOOR | TUNNEL))) {
                               found = 1;
                       }
               }
               if (found) {
                       monster = gr_monster((object *)0, 0);
                       if (!(monster->m_flags & IMITATES)) {
                               monster->m_flags |= WAKENS;
                       }
                       put_m_at(row, col, monster);
               }
       }
       for (i = 0; i < MONSTERS; i++) {
               mon_tab[i].first_level += (cur_level % 3);
       }
}

char
gmc_row_col(int row, int col)
{
       object *monster;

       if ((monster = object_at(&level_monsters, row, col)) != NULL) {
               if ((!(detect_monster || see_invisible || r_see_invisible) &&
                       (monster->m_flags & INVISIBLE)) || blind) {
                       return(monster->trail_char);
               }
               if (monster->m_flags & IMITATES) {
                       return(monster->disguise);
               }
               return(monster->m_char);
       } else {
               return('&');    /* BUG if this ever happens */
       }
}

char
gmc(object *monster)
{
       if ((!(detect_monster || see_invisible || r_see_invisible) &&
               (monster->m_flags & INVISIBLE))
               || blind) {
               return(monster->trail_char);
       }
       if (monster->m_flags & IMITATES) {
               return(monster->disguise);
       }
       return(monster->m_char);
}

void
mv_1_monster(object *monster, short row, short col)
{
       short i, n;
       boolean tried[6];

       if (monster->m_flags & ASLEEP) {
               if (monster->m_flags & NAPPING) {
                       if (--monster->nap_length <= 0) {
                               monster->m_flags &= (~(NAPPING | ASLEEP));
                       }
                       return;
               }
               if ((monster->m_flags & WAKENS) &&
                        rogue_is_around(monster->row, monster->col) &&
                        rand_percent(((stealthy > 0) ?
                               (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) :
                               WAKE_PERCENT))) {
                       wake_up(monster);
               }
               return;
       } else if (monster->m_flags & ALREADY_MOVED) {
               monster->m_flags &= (~ALREADY_MOVED);
               return;
       }
       if ((monster->m_flags & FLITS) && flit(monster)) {
               return;
       }
       if ((monster->m_flags & STATIONARY) &&
               (!mon_can_go(monster, rogue.row, rogue.col))) {
               return;
       }
       if (monster->m_flags & FREEZING_ROGUE) {
               return;
       }
       if ((monster->m_flags & CONFUSES) && m_confuse(monster)) {
               return;
       }
       if (mon_can_go(monster, rogue.row, rogue.col)) {
               mon_hit(monster);
               return;
       }
       if ((monster->m_flags & FLAMES) && flame_broil(monster)) {
               return;
       }
       if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) {
               return;
       }
       if ((monster->trow == monster->row) &&
                  (monster->tcol == monster->col)) {
               monster->trow = NO_ROOM;
       } else if (monster->trow != NO_ROOM) {
               row = monster->trow;
               col = monster->tcol;
       }
       if (monster->row > row) {
               row = monster->row - 1;
       } else if (monster->row < row) {
               row = monster->row + 1;
       }
       if ((dungeon[row][monster->col] & DOOR) &&
                mtry(monster, row, monster->col)) {
               return;
       }
       if (monster->col > col) {
               col = monster->col - 1;
       } else if (monster->col < col) {
               col = monster->col + 1;
       }
       if ((dungeon[monster->row][col] & DOOR) &&
                mtry(monster, monster->row, col)) {
               return;
       }
       if (mtry(monster, row, col)) {
               return;
       }

       for (i = 0; i <= 5; i++) tried[i] = 0;

       for (i = 0; i < 6; i++) {
NEXT_TRY:       n = get_rand(0, 5);
               switch(n) {
               case 0:
                       if (!tried[n] && mtry(monster, row, monster->col-1)) {
                               goto O;
                       }
                       break;
               case 1:
                       if (!tried[n] && mtry(monster, row, monster->col)) {
                               goto O;
                       }
                       break;
               case 2:
                       if (!tried[n] && mtry(monster, row, monster->col+1)) {
                               goto O;
                       }
                       break;
               case 3:
                       if (!tried[n] && mtry(monster, monster->row-1, col)) {
                               goto O;
                       }
                       break;
               case 4:
                       if (!tried[n] && mtry(monster, monster->row, col)) {
                               goto O;
                       }
                       break;
               case 5:
                       if (!tried[n] && mtry(monster, monster->row+1, col)) {
                               goto O;
                       }
                       break;
               }
               if (!tried[n]) {
                       tried[n] = 1;
               } else {
                       goto NEXT_TRY;
               }
       }
O:
       if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) {
               if (++(monster->o) > 4) {
                       if ((monster->trow == NO_ROOM) &&
                                       (!mon_sees(monster, rogue.row, rogue.col))) {
                               monster->trow = get_rand(1, (DROWS - 2));
                               monster->tcol = get_rand(0, (DCOLS - 1));
                       } else {
                               monster->trow = NO_ROOM;
                               monster->o = 0;
                       }
               }
       } else {
               monster->o_row = monster->row;
               monster->o_col = monster->col;
               monster->o = 0;
       }
}

static int
mtry(object *monster, short row, short col)
{
       if (mon_can_go(monster, row, col)) {
               move_mon_to(monster, row, col);
               return(1);
       }
       return(0);
}

void
move_mon_to(object *monster, short row, short col)
{
       short c;
       int mrow, mcol;

       mrow = monster->row;
       mcol = monster->col;

       dungeon[mrow][mcol] &= ~MONSTER;
       dungeon[row][col] |= MONSTER;

       c = mvinch(mrow, mcol);

       if ((c >= 'A') && (c <= 'Z')) {
               if (!detect_monster) {
                       mvaddch(mrow, mcol, monster->trail_char);
               } else {
                       if (rogue_can_see(mrow, mcol)) {
                               mvaddch(mrow, mcol, monster->trail_char);
                       } else {
                               if (monster->trail_char == '.') {
                                       monster->trail_char = ' ';
                               }
                               mvaddch(mrow, mcol, monster->trail_char);
                       }
               }
       }
       monster->trail_char = mvinch(row, col);
       if (!blind && (detect_monster || rogue_can_see(row, col))) {
               if ((!(monster->m_flags & INVISIBLE) ||
                       (detect_monster || see_invisible || r_see_invisible))) {
                       mvaddch(row, col, gmc(monster));
               }
       }
       if ((dungeon[row][col] & DOOR) &&
               (get_room_number(row, col) != cur_room) &&
               (dungeon[mrow][mcol] == FLOOR) && !blind) {
                       mvaddch(mrow, mcol, ' ');
       }
       if (dungeon[row][col] & DOOR) {
                       dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0),
                               row, col);
       } else {
               monster->row = row;
               monster->col = col;
       }
}

int
mon_can_go(const object *monster, short row, short col)
{
       object *obj;
       short dr, dc;

       dr = monster->row - row;        /* check if move distance > 1 */
       if ((dr >= 2) || (dr <= -2)) {
               return(0);
       }
       dc = monster->col - col;
       if ((dc >= 2) || (dc <= -2)) {
               return(0);
       }
       if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) {
               return(0);
       }
       if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) {
               return(0);
       }
       if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) ||
               (dungeon[monster->row][monster->col]&DOOR))) {
               return(0);
       }
       if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) &&
               (monster->trow == NO_ROOM)) {
               if ((monster->row < rogue.row) && (row < monster->row)) return(0);
               if ((monster->row > rogue.row) && (row > monster->row)) return(0);
               if ((monster->col < rogue.col) && (col < monster->col)) return(0);
               if ((monster->col > rogue.col) && (col > monster->col)) return(0);
       }
       if (dungeon[row][col] & OBJECT) {
               obj = object_at(&level_objects, row, col);
               if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) {
                       return(0);
               }
       }
       return(1);
}

void
wake_up(object *monster)
{
       if (!(monster->m_flags & NAPPING)) {
               monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
       }
}

void
wake_room(short rn, boolean entering, short row, short col)
{
       object *monster;
       short wake_percent;
       boolean in_room;

       wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT;
       if (stealthy > 0) {
               wake_percent /= (STEALTH_FACTOR + stealthy);
       }

       monster = level_monsters.next_monster;

       while (monster) {
               in_room = (rn == get_room_number(monster->row, monster->col));
               if (in_room) {
                       if (entering) {
                               monster->trow = NO_ROOM;
                       } else {
                               monster->trow = row;
                               monster->tcol = col;
                       }
               }
               if ((monster->m_flags & WAKENS) &&
                       (rn == get_room_number(monster->row, monster->col))) {
                       if (rand_percent(wake_percent)) {
                               wake_up(monster);
                       }
               }
               monster = monster->next_monster;
       }
}

const char *
mon_name(const object *monster)
{
       short ch;

       if (blind || ((monster->m_flags & INVISIBLE) &&
               !(detect_monster || see_invisible || r_see_invisible))) {
               return("something");
       }
       if (halluc) {
               ch = get_rand('A', 'Z') - 'A';
               return(m_names[ch]);
       }
       ch = monster->m_char - 'A';
       return(m_names[ch]);
}

static int
rogue_is_around(int row, int col)
{
       short rdif, cdif, retval;

       rdif = row - rogue.row;
       cdif = col - rogue.col;

       retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
       return(retval);
}

void
wanderer(void)
{
       object *monster;
       short row, col, i;
       boolean found = 0;

       monster = NULL;         /* XXXGCC -Wuninitialized [powerpc] */

       for (i = 0; ((i < 15) && (!found)); i++) {
               monster = gr_monster(NULL, 0);
               if (!(monster->m_flags & (WAKENS | WANDERS))) {
                       free_object(monster);
               } else {
                       found = 1;
               }
       }
       if (found) {
               found = 0;
               wake_up(monster);
               for (i = 0; ((i < 25) && (!found)); i++) {
                       gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
                       if (!rogue_can_see(row, col)) {
                               put_m_at(row, col, monster);
                               found = 1;
                       }
               }
               if (!found) {
                       free_object(monster);
               }
       }
}

void
show_monsters(void)
{
       object *monster;

       detect_monster = 1;

       if (blind) {
               return;
       }
       monster = level_monsters.next_monster;

       while (monster) {
               mvaddch(monster->row, monster->col, monster->m_char);
               if (monster->m_flags & IMITATES) {
                       monster->m_flags &= (~IMITATES);
                       monster->m_flags |= WAKENS;
               }
               monster = monster->next_monster;
       }
}

void
create_monster(void)
{
       short row, col;
       short i;
       boolean found = 0;
       object *monster;

       row = rogue.row;
       col = rogue.col;

       for (i = 0; i < 9; i++) {
               rand_around(i, &row, &col);
               if (((row == rogue.row) && (col == rogue.col)) ||
                               (row < MIN_ROW) || (row > (DROWS-2)) ||
                               (col < 0) || (col > (DCOLS-1))) {
                       continue;
               }
               if ((!(dungeon[row][col] & MONSTER)) &&
                         (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) {
                       found = 1;
                       break;
               }
       }
       if (found) {
               monster = gr_monster((object *)0, 0);
               put_m_at(row, col, monster);
               mvaddch(row, col, gmc(monster));
               if (monster->m_flags & (WANDERS | WAKENS)) {
                       wake_up(monster);
               }
       } else {
               messagef(0, "you hear a faint cry of anguish in the distance");
       }
}

static void
put_m_at(short row, short col, object *monster)
{
       monster->row = row;
       monster->col = col;
       dungeon[row][col] |= MONSTER;
       monster->trail_char = mvinch(row, col);
       (void)add_to_pack(monster, &level_monsters, 0);
       aim_monster(monster);
}

static void
aim_monster(object *monster)
{
       short i, rn, d, r;

       rn = get_room_number(monster->row, monster->col);
       if (rn == NO_ROOM)
               clean_up("aim_monster: monster not in room");
       r = get_rand(0, 12);

       for (i = 0; i < 4; i++) {
               d = (r + i) % 4;
               if (rooms[rn].doors[d].oth_room != NO_ROOM) {
                       monster->trow = rooms[rn].doors[d].door_row;
                       monster->tcol = rooms[rn].doors[d].door_col;
                       break;
               }
       }
}

int
rogue_can_see(int row, int col)
{
       int retval;

       retval = !blind &&
                       (((get_room_number(row, col) == cur_room) &&
                                       !(rooms[cur_room].is_room & R_MAZE)) ||
                       rogue_is_around(row, col));

       return(retval);
}

static int
move_confused(object *monster)
{
       short i, row, col;

       if (!(monster->m_flags & ASLEEP)) {
               if (--monster->moves_confused <= 0) {
                       monster->m_flags &= (~CONFUSED);
               }
               if (monster->m_flags & STATIONARY) {
                       return(coin_toss() ? 1 : 0);
               } else if (rand_percent(15)) {
                       return(1);
               }
               row = monster->row;
               col = monster->col;

               for (i = 0; i < 9; i++) {
                       rand_around(i, &row, &col);
                       if ((row == rogue.row) && (col == rogue.col)) {
                               return(0);
                       }
                       if (mtry(monster, row, col)) {
                               return(1);
                       }
               }
       }
       return(0);
}

static int
flit(object *monster)
{
       short i, row, col;

       if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
               return(0);
       }
       if (rand_percent(10)) {
               return(1);
       }
       row = monster->row;
       col = monster->col;

       for (i = 0; i < 9; i++) {
               rand_around(i, &row, &col);
               if ((row == rogue.row) && (col == rogue.col)) {
                       continue;
               }
               if (mtry(monster, row, col)) {
                       return(1);
               }
       }
       return(1);
}

char
gr_obj_char(void)
{
       short r;
       const char *rs = "%!?]=/):*";

       r = get_rand(0, 8);

       return(rs[r]);
}

static int
no_room_for_monster(int rn)
{
       short i, j;

       for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
               for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
                       if (!(dungeon[i][j] & MONSTER)) {
                               return(0);
                       }
               }
       }
       return(1);
}

void
aggravate(void)
{
       object *monster;

       messagef(0, "you hear a high pitched humming noise");

       monster = level_monsters.next_monster;

       while (monster) {
               wake_up(monster);
               monster->m_flags &= (~IMITATES);
               if (rogue_can_see(monster->row, monster->col)) {
                       mvaddch(monster->row, monster->col, monster->m_char);
               }
               monster = monster->next_monster;
       }
}

boolean
mon_sees(const object *monster, int row, int col)
{
       short rn, rdif, cdif, retval;

       rn = get_room_number(row, col);

       if (    (rn != NO_ROOM) &&
                       (rn == get_room_number(monster->row, monster->col)) &&
                       !(rooms[rn].is_room & R_MAZE)) {
               return(1);
       }
       rdif = row - monster->row;
       cdif = col - monster->col;

       retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
       return(retval);
}

void
mv_aquatars(void)
{
       object *monster;

       monster = level_monsters.next_monster;

       while (monster) {
               if ((monster->m_char == 'A') &&
                       mon_can_go(monster, rogue.row, rogue.col)) {
                       mv_1_monster(monster, rogue.row, rogue.col);
                       monster->m_flags |= ALREADY_MOVED;
               }
               monster = monster->next_monster;
       }
}