2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * @(#)monster.c 8.1 (Berkeley) 5/31/93
37 * $FreeBSD: src/games/rogue/monster.c,v 1.6 1999/11/30 03:49:24 billf Exp $
38 * $DragonFly: src/games/rogue/monster.c,v 1.3 2006/09/02 19:31:07 pavalos Exp $
44 * This source herein may be modified and/or distributed by anybody who
45 * so desires, with the following restrictions:
46 * 1.) No portion of this notice shall be removed.
47 * 2.) Credit shall not be taken for the creation of this source.
48 * 3.) This code is not to be traded, sold, or used for personal
55 static boolean mtry(object *, short, short);
56 static short rogue_is_around(int, int);
57 static void put_m_at(short, short, object *);
58 static void aim_monster(object *);
59 static boolean move_confused(object *);
60 static boolean flit(object *);
61 static boolean no_room_for_monster(int);
63 object level_monsters;
64 boolean mon_disappeared;
66 const char *const m_names[] = {
95 object mon_tab[MONSTERS] = {
96 {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
97 {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
98 {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
99 {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
100 {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
101 {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
102 {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
103 2000,20,126,85,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
104 {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
105 {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
106 {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
107 {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
108 {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
109 {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
110 250,18,126,85,0,25,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
111 {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
112 {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
113 {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
114 {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
115 {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
116 {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
117 {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
118 {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
119 200,17,26,85,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
120 {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
121 350,19,126,85,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
122 {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
123 {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
124 {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL},
125 {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}
128 extern short cur_level;
129 extern short cur_room, party_room;
130 extern short blind, halluc, haste_self;
131 extern boolean detect_monster, see_invisible, r_see_invisible;
132 extern short stealthy;
144 for (i = 0; i < n; i++) {
145 monster = gr_monster((object *) 0, 0);
146 if ((monster->m_flags & WANDERS) && coin_toss()) {
149 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
150 put_m_at(row, col, monster);
155 gr_monster(object *monster, int mn)
158 monster = alloc_object();
161 mn = get_rand(0, MONSTERS-1);
162 if ((cur_level >= mon_tab[mn].first_level) &&
163 (cur_level <= mon_tab[mn].last_level)) {
168 *monster = mon_tab[mn];
169 if (monster->m_flags & IMITATES) {
170 monster->disguise = gr_obj_char();
172 if (cur_level > (AMULET_LEVEL + 2)) {
173 monster->m_flags |= HASTED;
175 monster->trow = NO_ROOM;
182 object *monster, *next_monster, *test_mons;
185 if (haste_self % 2) {
189 monster = level_monsters.next_monster;
192 next_monster = monster->next_monster;
194 if (monster->m_flags & HASTED) {
195 mv_1_monster(monster, rogue.row, rogue.col);
196 if (mon_disappeared) {
199 } else if (monster->m_flags & SLOWED) {
200 monster->slowed_toggle = !monster->slowed_toggle;
201 if (monster->slowed_toggle) {
205 if ((monster->m_flags & CONFUSED) && move_confused(monster)) {
209 if ( (monster->m_flags & FLIES) &&
210 !(monster->m_flags & NAPPING) &&
211 !mon_can_go(monster, rogue.row, rogue.col)) {
213 mv_1_monster(monster, rogue.row, rogue.col);
214 if (mon_disappeared) {
218 if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) {
219 mv_1_monster(monster, rogue.row, rogue.col);
221 NM: test_mons = level_monsters.next_monster;
225 if(next_monster == test_mons)
227 monster = next_monster;
230 test_mons = test_mons->next_monster;
236 party_monsters(int rn, int n)
245 for (i = 0; i < MONSTERS; i++) {
246 mon_tab[i].first_level -= (cur_level % 3);
248 for (i = 0; i < n; i++) {
249 if (no_room_for_monster(rn)) {
252 for (j = found = 0; ((!found) && (j < 250)); j++) {
253 row = get_rand(rooms[rn].top_row+1,
254 rooms[rn].bottom_row-1);
255 col = get_rand(rooms[rn].left_col+1,
256 rooms[rn].right_col-1);
257 if ((!(dungeon[row][col] & MONSTER)) &&
258 (dungeon[row][col] & (FLOOR | TUNNEL))) {
263 monster = gr_monster((object *) 0, 0);
264 if (!(monster->m_flags & IMITATES)) {
265 monster->m_flags |= WAKENS;
267 put_m_at(row, col, monster);
270 for (i = 0; i < MONSTERS; i++) {
271 mon_tab[i].first_level += (cur_level % 3);
276 gmc_row_col(int row, int col)
280 if ((monster = object_at(&level_monsters, row, col))) {
281 if ((!(detect_monster || see_invisible || r_see_invisible) &&
282 (monster->m_flags & INVISIBLE)) || blind) {
283 return(monster->trail_char);
285 if (monster->m_flags & IMITATES) {
286 return(monster->disguise);
288 return(monster->m_char);
290 return('&'); /* BUG if this ever happens */
297 if ((!(detect_monster || see_invisible || r_see_invisible) &&
298 (monster->m_flags & INVISIBLE))
300 return(monster->trail_char);
302 if (monster->m_flags & IMITATES) {
303 return(monster->disguise);
305 return(monster->m_char);
309 mv_1_monster(object *monster, short row, short col)
314 if (monster->m_flags & ASLEEP) {
315 if (monster->m_flags & NAPPING) {
316 if (--monster->nap_length <= 0) {
317 monster->m_flags &= (~(NAPPING | ASLEEP));
321 if ((monster->m_flags & WAKENS) &&
322 rogue_is_around(monster->row, monster->col) &&
323 rand_percent(((stealthy > 0) ?
324 (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) :
329 } else if (monster->m_flags & ALREADY_MOVED) {
330 monster->m_flags &= (~ALREADY_MOVED);
333 if ((monster->m_flags & FLITS) && flit(monster)) {
336 if ((monster->m_flags & STATIONARY) &&
337 (!mon_can_go(monster, rogue.row, rogue.col))) {
340 if (monster->m_flags & FREEZING_ROGUE) {
343 if ((monster->m_flags & CONFUSES) && m_confuse(monster)) {
346 if (mon_can_go(monster, rogue.row, rogue.col)) {
350 if ((monster->m_flags & FLAMES) && flame_broil(monster)) {
353 if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) {
356 if ((monster->trow == monster->row) &&
357 (monster->tcol == monster->col)) {
358 monster->trow = NO_ROOM;
359 } else if (monster->trow != NO_ROOM) {
363 if (monster->row > row) {
364 row = monster->row - 1;
365 } else if (monster->row < row) {
366 row = monster->row + 1;
368 if ((dungeon[row][monster->col] & DOOR) &&
369 mtry(monster, row, monster->col)) {
372 if (monster->col > col) {
373 col = monster->col - 1;
374 } else if (monster->col < col) {
375 col = monster->col + 1;
377 if ((dungeon[monster->row][col] & DOOR) &&
378 mtry(monster, monster->row, col)) {
381 if (mtry(monster, row, col)) {
385 for (i = 0; i <= 5; i++) tried[i] = 0;
387 for (i = 0; i < 6; i++) {
388 NEXT_TRY: n = get_rand(0, 5);
391 if (!tried[n] && mtry(monster, row, monster->col-1)) {
396 if (!tried[n] && mtry(monster, row, monster->col)) {
401 if (!tried[n] && mtry(monster, row, monster->col+1)) {
406 if (!tried[n] && mtry(monster, monster->row-1, col)) {
411 if (!tried[n] && mtry(monster, monster->row, col)) {
416 if (!tried[n] && mtry(monster, monster->row+1, col)) {
428 if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) {
429 if (++(monster->o) > 4) {
430 if ((monster->trow == NO_ROOM) &&
431 (!mon_sees(monster, rogue.row, rogue.col))) {
432 monster->trow = get_rand(1, (DROWS - 2));
433 monster->tcol = get_rand(0, (DCOLS - 1));
435 monster->trow = NO_ROOM;
440 monster->o_row = monster->row;
441 monster->o_col = monster->col;
447 mtry(object *monster, short row, short col)
449 if (mon_can_go(monster, row, col)) {
450 move_mon_to(monster, row, col);
457 move_mon_to(object *monster, short row, short col)
465 dungeon[mrow][mcol] &= ~MONSTER;
466 dungeon[row][col] |= MONSTER;
468 c = mvinch(mrow, mcol);
470 if ((c >= 'A') && (c <= 'Z')) {
471 if (!detect_monster) {
472 mvaddch(mrow, mcol, monster->trail_char);
474 if (rogue_can_see(mrow, mcol)) {
475 mvaddch(mrow, mcol, monster->trail_char);
477 if (monster->trail_char == '.') {
478 monster->trail_char = ' ';
480 mvaddch(mrow, mcol, monster->trail_char);
484 monster->trail_char = mvinch(row, col);
485 if (!blind && (detect_monster || rogue_can_see(row, col))) {
486 if ((!(monster->m_flags & INVISIBLE) ||
487 (detect_monster || see_invisible || r_see_invisible))) {
488 mvaddch(row, col, gmc(monster));
491 if ((dungeon[row][col] & DOOR) &&
492 (get_room_number(row, col) != cur_room) &&
493 (dungeon[mrow][mcol] == FLOOR) && !blind) {
494 mvaddch(mrow, mcol, ' ');
496 if (dungeon[row][col] & DOOR) {
497 dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0),
506 mon_can_go(const object *monster, short row, short col)
511 dr = monster->row - row; /* check if move distance > 1 */
512 if ((dr >= 2) || (dr <= -2)) {
515 dc = monster->col - col;
516 if ((dc >= 2) || (dc <= -2)) {
519 if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) {
522 if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) {
525 if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) ||
526 (dungeon[monster->row][monster->col]&DOOR))) {
529 if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) &&
530 (monster->trow == NO_ROOM)) {
531 if ((monster->row < rogue.row) && (row < monster->row)) return(0);
532 if ((monster->row > rogue.row) && (row > monster->row)) return(0);
533 if ((monster->col < rogue.col) && (col < monster->col)) return(0);
534 if ((monster->col > rogue.col) && (col > monster->col)) return(0);
536 if (dungeon[row][col] & OBJECT) {
537 obj = object_at(&level_objects, row, col);
538 if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) {
546 wake_up(object *monster)
548 if (!(monster->m_flags & NAPPING)) {
549 monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
554 wake_room(short rn, boolean entering, short row, short col)
560 wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT;
562 wake_percent /= (STEALTH_FACTOR + stealthy);
565 monster = level_monsters.next_monster;
568 in_room = (rn == get_room_number(monster->row, monster->col));
571 monster->trow = NO_ROOM;
577 if ((monster->m_flags & WAKENS) &&
578 (rn == get_room_number(monster->row, monster->col))) {
579 if (rand_percent(wake_percent)) {
583 monster = monster->next_monster;
588 mon_name(const object *monster)
592 if (blind || ((monster->m_flags & INVISIBLE) &&
593 !(detect_monster || see_invisible || r_see_invisible))) {
597 ch = get_rand('A', 'Z') - 'A';
600 ch = monster->m_char - 'A';
605 rogue_is_around(int row, int col)
607 short rdif, cdif, retval;
609 rdif = row - rogue.row;
610 cdif = col - rogue.col;
612 retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
623 for (i = 0; ((i < 15) && (!found)); i++) {
624 monster = gr_monster((object *) 0, 0);
625 if (!(monster->m_flags & (WAKENS | WANDERS))) {
626 free_object(monster);
634 for (i = 0; ((i < 25) && (!found)); i++) {
635 gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
636 if (!rogue_can_see(row, col)) {
637 put_m_at(row, col, monster);
642 free_object(monster);
657 monster = level_monsters.next_monster;
660 mvaddch(monster->row, monster->col, monster->m_char);
661 if (monster->m_flags & IMITATES) {
662 monster->m_flags &= (~IMITATES);
663 monster->m_flags |= WAKENS;
665 monster = monster->next_monster;
680 for (i = 0; i < 9; i++) {
681 rand_around(i, &row, &col);
682 if (((row == rogue.row) && (col = rogue.col)) ||
683 (row < MIN_ROW) || (row > (DROWS-2)) ||
684 (col < 0) || (col > (DCOLS-1))) {
687 if ((!(dungeon[row][col] & MONSTER)) &&
688 (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) {
694 monster = gr_monster((object *) 0, 0);
695 put_m_at(row, col, monster);
696 mvaddch(row, col, gmc(monster));
697 if (monster->m_flags & (WANDERS | WAKENS)) {
701 message("you hear a faint cry of anguish in the distance", 0);
706 put_m_at(short row, short col, object *monster)
710 dungeon[row][col] |= MONSTER;
711 monster->trail_char = mvinch(row, col);
712 add_to_pack(monster, &level_monsters, 0);
713 aim_monster(monster);
717 aim_monster(object *monster)
721 rn = get_room_number(monster->row, monster->col);
724 for (i = 0; i < 4; i++) {
726 if (rooms[rn].doors[d].oth_room != NO_ROOM) {
727 monster->trow = rooms[rn].doors[d].door_row;
728 monster->tcol = rooms[rn].doors[d].door_col;
735 rogue_can_see(int row, int col)
740 (((get_room_number(row, col) == cur_room) &&
741 !(rooms[cur_room].is_room & R_MAZE)) ||
742 rogue_is_around(row, col));
748 move_confused(object *monster)
752 if (!(monster->m_flags & ASLEEP)) {
753 if (--monster->moves_confused <= 0) {
754 monster->m_flags &= (~CONFUSED);
756 if (monster->m_flags & STATIONARY) {
757 return(coin_toss() ? 1 : 0);
758 } else if (rand_percent(15)) {
764 for (i = 0; i < 9; i++) {
765 rand_around(i, &row, &col);
766 if ((row == rogue.row) && (col == rogue.col)) {
769 if (mtry(monster, row, col)) {
778 flit(object *monster)
782 if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
785 if (rand_percent(10)) {
791 for (i = 0; i < 9; i++) {
792 rand_around(i, &row, &col);
793 if ((row == rogue.row) && (col == rogue.col)) {
796 if (mtry(monster, row, col)) {
807 const char *rs = "%!?]=/):*";
815 no_room_for_monster(int rn)
819 for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
820 for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
821 if (!(dungeon[i][j] & MONSTER)) {
834 message("you hear a high pitched humming noise", 0);
836 monster = level_monsters.next_monster;
840 monster->m_flags &= (~IMITATES);
841 if (rogue_can_see(monster->row, monster->col)) {
842 mvaddch(monster->row, monster->col, monster->m_char);
844 monster = monster->next_monster;
849 mon_sees(const object *monster, int row, int col)
851 short rn, rdif, cdif, retval;
853 rn = get_room_number(row, col);
855 if ( (rn != NO_ROOM) &&
856 (rn == get_room_number(monster->row, monster->col)) &&
857 !(rooms[rn].is_room & R_MAZE)) {
860 rdif = row - monster->row;
861 cdif = col - monster->col;
863 retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
872 monster = level_monsters.next_monster;
875 if ((monster->m_char == 'A') &&
876 mon_can_go(monster, rogue.row, rogue.col)) {
877 mv_1_monster(monster, rogue.row, rogue.col);
878 monster->m_flags |= ALREADY_MOVED;
880 monster = monster->next_monster;