Home | History | Annotate | Line # | Download | only in rogue
      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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    557 wake_up(object *monster)
    558 {
    559 	if (!(monster->m_flags & NAPPING)) {
    560 		monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
    561 	}
    562 }
    563 
    564 void
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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