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