Mercurial > hg > early-roguelike
view arogue5/monsters.c @ 227:696277507a2e
Rogue V4, V5: disable a cheat granting permanent monster detection.
In these two games, a potion of monster detection turns on the player's
SEEMONST flag. A fuse is set to call turn_see() to turn the flag back
off. But the save and restore functions do not recognize turn_see() and
fail to set the fuse up again.
When restoring, Rogue V4 merely sets the fuse's function to NULL and
leaves it burning. When it goes off, a segfault results. Rogue V5
clears all the fuse's fields, and the player retains the ability to see
all monsters on the level.
The save and restore code can now handle the fuse. The function used is
a new wrapper, turn_see_off(), which should lead to less problems with
daemons being multiple incompatible types.
Also, Rogue V4 and Super-Rogue now properly clear unrecognized daemon
and fuse slots when restoring a saved game.
author | John "Elwin" Edwards |
---|---|
date | Sat, 05 Mar 2016 12:10:20 -0500 |
parents | 56e748983fa8 |
children | e52a8a7ad4c5 |
line wrap: on
line source
/* * File with various monster functions in it * * Advanced Rogue * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T * All rights reserved. * * Based on "Rogue: Exploring the Dungeons of Doom" * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman * All rights reserved. * * See the file LICENSE.TXT for full copyright and licensing information. */ #include "curses.h" #include "rogue.h" #include <stdlib.h> #include <ctype.h> #include <string.h> /* * Check_residue takes care of any effect of the monster */ void check_residue(struct thing *tp) { /* * Take care of special abilities */ if (on(*tp, DIDHOLD) && (--hold_count == 0)) turn_off(player, ISHELD); /* If it has lowered player, give him back a level */ if (on(*tp, DIDDRAIN)) raise_level(FALSE); /* If frightened of this monster, stop */ if (on(player, ISFLEE) && player.t_dest == &tp->t_pos) turn_off(player, ISFLEE); /* If monster was suffocating player, stop it */ if (on(*tp, DIDSUFFOCATE)) extinguish(suffocate); /* If something with fire, may darken */ if (on(*tp, HASFIRE)) { register struct room *rp=roomin(&tp->t_pos); register struct linked_list *fire_item; if (rp) { for (fire_item = rp->r_fires; fire_item != NULL; fire_item = next(fire_item)) { if (THINGPTR(fire_item) == tp) { detach(rp->r_fires, fire_item); destroy_item(fire_item); if (rp->r_fires == NULL) { rp->r_flags &= ~HASFIRE; if (cansee(tp->t_pos.y, tp->t_pos.x)) light(&hero); } break; } } } } } /* * Creat_mons creates the specified monster -- any if 0 * person: Where to create next to */ bool creat_mons(struct thing *person, short monster, bool report) { struct linked_list *nitem; register struct thing *tp; struct room *rp; coord *mp; if (levtype == POSTLEV) return(FALSE); if ((mp = fallpos(&(person->t_pos), FALSE, 2)) != NULL) { nitem = new_item(sizeof (struct thing)); new_monster(nitem, monster == 0 ? randmonster(FALSE, FALSE) : monster, mp, TRUE); tp = THINGPTR(nitem); runto(tp, &hero); tp->t_no_move = 1; /* since it just got here, it is disoriented */ carry_obj(tp, monsters[tp->t_index].m_carry/2); /* only half chance */ if (on(*tp, HASFIRE)) { rp = roomin(&tp->t_pos); if (rp) { register struct linked_list *fire_item; /* Put the new fellow in the room list */ fire_item = creat_item(); ldata(fire_item) = (char *) tp; attach(rp->r_fires, fire_item); rp->r_flags |= HASFIRE; } } /* * If we can see this monster, set oldch to ' ' to make light() * think the creature used to be invisible (ie. not seen here) */ if (cansee(tp->t_pos.y, tp->t_pos.x)) tp->t_oldch = ' '; return(TRUE); } if (report) msg("You hear a faint cry of anguish in the distance."); return(FALSE); } /* * Genmonsters: * Generate at least 'least' monsters for this single room level. * 'Treas' indicates whether this is a "treasure" level. */ void genmonsters(int least, bool treas) { reg int i; reg struct room *rp = &rooms[0]; reg struct linked_list *item; reg struct thing *mp; coord tp; for (i = 0; i < level + least; i++) { if (!treas && rnd(100) < 50) /* put in some little buggers */ continue; /* * Put the monster in */ item = new_item(sizeof *mp); mp = THINGPTR(item); do { rnd_pos(rp, &tp); } until(mvwinch(stdscr, tp.y, tp.x) == FLOOR); new_monster(item, randmonster(FALSE, FALSE), &tp, FALSE); /* * See if we want to give it a treasure to carry around. */ carry_obj(mp, monsters[mp->t_index].m_carry); /* Is it going to give us some light? */ if (on(*mp, HASFIRE)) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) mp; attach(rp->r_fires, fire_item); rp->r_flags |= HASFIRE; } } } /* * id_monst returns the index of the monster given its letter */ short id_monst(char monster) { register short result; result = NLEVMONS*vlevel; if (result > NUMMONST) result = NUMMONST; for(; result>0; result--) if (monsters[result].m_appear == monster) return(result); for (result=(NLEVMONS*vlevel)+1; result <= NUMMONST; result++) if (monsters[result].m_appear == monster) return(result); return(0); } /* * new_monster: * Pick a new monster and add it to the list */ void new_monster(struct linked_list *item, short type, coord *cp, bool max_monster) { register struct thing *tp; register struct monster *mp; register char *ip, *hitp; register int i, min_intel, max_intel; register int num_dice, num_sides=8, num_extra=0; attach(mlist, item); tp = THINGPTR(item); tp->t_turn = TRUE; tp->t_pack = NULL; tp->t_index = type; tp->t_wasshot = FALSE; tp->t_type = monsters[type].m_appear; tp->t_ctype = C_MONSTER; tp->t_no_move = 0; tp->t_doorgoal = 0; tp->t_quiet = 0; tp->t_pos = tp->t_oldpos = *cp; tp->t_oldch = CCHAR( mvwinch(cw, cp->y, cp->x) ); mvwaddch(mw, cp->y, cp->x, tp->t_type); mp = &monsters[tp->t_index]; /* Figure out monster's hit points */ hitp = mp->m_stats.s_hpt; num_dice = atoi(hitp); if ((hitp = strchr(hitp, 'd')) != NULL) { num_sides = atoi(++hitp); if ((hitp = strchr(hitp, '+')) != NULL) num_extra = atoi(++hitp); } tp->t_stats.s_lvl = mp->m_stats.s_lvl; tp->t_stats.s_arm = mp->m_stats.s_arm; strncpy(tp->t_stats.s_dmg,mp->m_stats.s_dmg,sizeof(tp->t_stats.s_dmg)); tp->t_stats.s_str = mp->m_stats.s_str; if (vlevel > HARDER) { /* the deeper, the meaner we get */ tp->t_stats.s_lvl += (vlevel - HARDER); num_dice += (vlevel - HARDER)/2; } if (max_monster) tp->t_stats.s_hpt = num_dice * num_sides + num_extra; else tp->t_stats.s_hpt = roll(num_dice, num_sides) + num_extra; tp->t_stats.s_exp = mp->m_stats.s_exp + mp->m_add_exp*tp->t_stats.s_hpt; /* * just initailize others values to something reasonable for now * maybe someday will *really* put these in monster table */ tp->t_stats.s_wisdom = 8 + rnd(4); tp->t_stats.s_dext = 8 + rnd(4); tp->t_stats.s_const = 8 + rnd(4); tp->t_stats.s_charisma = 8 + rnd(4); /* Set the initial flags */ for (i=0; i<16; i++) tp->t_flags[i] = 0; for (i=0; i<MAXFLAGS; i++) turn_on(*tp, mp->m_flags[i]); /* suprising monsters don't always surprise you */ if (!max_monster && on(*tp, CANSURPRISE) && off(*tp, ISUNIQUE) && rnd(100) < 20) turn_off(*tp, CANSURPRISE); /* If this monster is unique, gen it */ if (on(*tp, ISUNIQUE)) mp->m_normal = FALSE; /* * if is it the quartermaster, then compute his level and exp pts * based on the level. This will make it fair when thieves try to * steal and give them reasonable experience if they succeed. */ if (on(*tp, CANSELL)) { tp->t_stats.s_exp = vlevel * 100; tp->t_stats.s_lvl = vlevel/2 + 1; attach(tp->t_pack, new_thing(ALL)); } /* Normally scared monsters have a chance to not be scared */ if (on(*tp, ISFLEE) && (rnd(4) == 0)) turn_off(*tp, ISFLEE); /* Figure intelligence */ min_intel = atoi(mp->m_intel); if ((ip = (char *) strchr(mp->m_intel, '-')) == NULL) tp->t_stats.s_intel = min_intel; else { max_intel = atoi(++ip); if (max_monster) tp->t_stats.s_intel = max_intel; else tp->t_stats.s_intel = min_intel + rnd(max_intel - min_intel); } if (vlevel > HARDER) tp->t_stats.s_intel += ((vlevel - HARDER)/2); tp->maxstats = tp->t_stats; /* If the monster can shoot, it may have a weapon */ if (on(*tp, CANSHOOT) && ((rnd(100) < (22 + vlevel)) || max_monster)) { struct linked_list *item1; register struct object *cur, *cur1; item = new_item(sizeof *cur); item1 = new_item(sizeof *cur1); cur = OBJPTR(item); cur1 = OBJPTR(item1); cur->o_hplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); cur->o_dplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); cur1->o_hplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); cur1->o_dplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); strcpy(cur->o_damage,"0d0"); strcpy(cur->o_hurldmg,"0d0"); strcpy(cur1->o_damage,"0d0"); strcpy(cur1->o_hurldmg,"0d0"); cur->o_ac = cur1->o_ac = 11; cur->o_count = cur1->o_count = 1; cur->o_group = cur1->o_group = 0; cur->contents = cur1->contents = NULL; if ((cur->o_hplus <= 0) && (cur->o_dplus <= 0)) cur->o_flags = ISCURSED; if ((cur1->o_hplus <= 0) && (cur1->o_dplus <= 0)) cur1->o_flags = ISCURSED; cur->o_flags = cur1->o_flags = 0; cur->o_type = cur1->o_type = WEAPON; cur->o_mark[0] = cur1->o_mark[0] = '\0'; /* The monster may use a crossbow, sling, or an arrow */ i = rnd(100); if (i < 10) { cur->o_which = CROSSBOW; cur1->o_which = BOLT; init_weapon(cur, CROSSBOW); init_weapon(cur1, BOLT); } else if (i < 70) { cur->o_which = BOW; cur1->o_which = ARROW; init_weapon(cur, BOW); init_weapon(cur1, ARROW); } else { cur->o_which = SLING; cur1->o_which = ROCK; init_weapon(cur, SLING); init_weapon(cur1, ROCK); } attach(tp->t_pack, item); attach(tp->t_pack, item1); } if (ISWEARING(R_AGGR)) runto(tp, &hero); if (on(*tp, ISDISGUISE)) { char mch = 0; if (tp->t_pack != NULL) mch = (OBJPTR(tp->t_pack))->o_type; else switch (rnd(10)) { case 0: mch = GOLD; when 1: mch = POTION; when 2: mch = SCROLL; when 3: mch = FOOD; when 4: mch = WEAPON; when 5: mch = ARMOR; when 6: mch = RING; when 7: mch = STICK; when 8: mch = monsters[randmonster(FALSE, FALSE)].m_appear; when 9: mch = MM; } tp->t_disguise = mch; } } /* * randmonster: * Pick a monster to show up. The lower the level, * the meaner the monster. */ short randmonster(bool wander, bool no_unique) { register int d, cur_level, range, i; /* * Do we want a merchant? Merchant is always in place 'NUMMONST' */ if (wander && monsters[NUMMONST].m_wander && rnd(100) < 3) return NUMMONST; cur_level = vlevel; range = 4*NLEVMONS; i = 0; do { if (i++ > range*10) { /* just in case all have be genocided */ i = 0; if (--cur_level <= 0) fatal("Rogue could not find a monster to make"); } d = NLEVMONS*(cur_level - 1) + (rnd(range) - (range - 1 - NLEVMONS)); if (d < 1) d = rnd(NLEVMONS) + 1; if (d > NUMMONST - NUMUNIQUE - 1) { if (no_unique) d = rnd(range) + (NUMMONST - NUMUNIQUE - 1) - (range - 1); else if (d > NUMMONST - 1) d = rnd(range+NUMUNIQUE) + (NUMMONST-1) - (range+NUMUNIQUE-1); } } while (wander ? !monsters[d].m_wander || !monsters[d].m_normal : !monsters[d].m_normal); return d; } /* Sell displays a menu of goods from which the player may choose * to purchase something. */ void sell(struct thing *tp) { register struct linked_list *item; register struct object *obj; register int i, j, min_worth, nitems, goods = 0, chance, which_item; char buffer[LINELEN]; struct { int which; int plus1, plus2; int count; int worth; char *name; } selection[10]; min_worth = 100000; item = find_mons(tp->t_pos.y, tp->t_pos.x); /* Get pointer to monster */ /* Select the items */ nitems = rnd(6) + 5; for (i=0; i<nitems; i++) { selection[i].worth = selection[i].plus1 = selection[i].plus2 = selection[i].which = selection[i].count = 0; } switch (rnd(9)) { /* Armor */ case 0: case 1: goods = ARMOR; for (i=0; i<nitems; i++) { chance = rnd(100); for (j = 0; j < MAXARMORS; j++) if (chance < armors[j].a_prob) break; if (j == MAXARMORS) { debug("Picked a bad armor %d", chance); j = 0; } selection[i].which = j; selection[i].count = 1; if (rnd(100) < 40) selection[i].plus1 = rnd(5) + 1; else selection[i].plus1 = 0; selection[i].name = armors[j].a_name; /* Calculate price */ selection[i].worth = armors[j].a_worth; selection[i].worth += 2 * s_magic[S_ALLENCH].mi_worth * selection[i].plus1; if (min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* Weapon */ case 2: case 3: goods = WEAPON; for (i=0; i<nitems; i++) { selection[i].which = rnd(MAXWEAPONS); selection[i].count = 1; if (rnd(100) < 35) { selection[i].plus1 = rnd(3); selection[i].plus2 = rnd(3); } else { selection[i].plus1 = 0; selection[i].plus2 = 0; } if (weaps[selection[i].which].w_flags & ISMANY) selection[i].count = rnd(15) + 5; selection[i].name = weaps[selection[i].which].w_name; /* * note: use "count" before adding in the enchantment cost * of an item. This will keep the price of arrows * and such to a reasonable price. */ j = selection[i].plus1 + selection[i].plus2; selection[i].worth = weaps[selection[i].which].w_worth; selection[i].worth *= selection[i].count; selection[i].worth += 2 * s_magic[S_ALLENCH].mi_worth * j; if (min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* Staff or wand */ case 4: goods = STICK; for (i=0; i<nitems; i++) { selection[i].which = pick_one(ws_magic, MAXSTICKS); selection[i].plus1 = rnd(11) + 5; /* Charges */ selection[i].count = 1; selection[i].name = ws_magic[selection[i].which].mi_name; selection[i].worth = ws_magic[selection[i].which].mi_worth; selection[i].worth += 20 * selection[i].plus1; if (min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* Ring */ case 5: goods = RING; for (i=0; i<nitems; i++) { selection[i].which = pick_one(r_magic, MAXRINGS); selection[i].plus1 = rnd(2) + 1; /* Armor class */ selection[i].count = 1; if (rnd(100) < r_magic[selection[i].which].mi_bless + 10) selection[i].plus1 += rnd(2) + 1; selection[i].name = r_magic[selection[i].which].mi_name; selection[i].worth = r_magic[selection[i].which].mi_worth; switch (selection[i].which) { case R_DIGEST: if (selection[i].plus1 > 2) selection[i].plus1 = 2; else if (selection[i].plus1 < 1) selection[i].plus1 = 1; /* fall thru here to other cases */ case R_ADDSTR: case R_ADDDAM: case R_PROTECT: case R_ADDHIT: case R_ADDINTEL: case R_ADDWISDOM: if (selection[i].plus1 > 0) selection[i].worth += selection[i].plus1 * 50; } if(min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* scroll */ case 6: goods = SCROLL; for (i=0; i<nitems; i++) { selection[i].which = pick_one(s_magic, MAXSCROLLS); selection[i].count = 1; selection[i].name = s_magic[selection[i].which].mi_name; selection[i].worth = s_magic[selection[i].which].mi_worth; if (min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* potions */ case 7: goods = POTION; for (i=0; i<nitems; i++) { selection[i].which = pick_one(p_magic, MAXPOTIONS); selection[i].count = 1; selection[i].name = p_magic[selection[i].which].mi_name; selection[i].worth = p_magic[selection[i].which].mi_worth; if (min_worth > selection[i].worth) min_worth = selection[i].worth; } break; /* Miscellaneous magic */ case 8: goods = MM; for (i=0; i<nitems; i++) { /* don't sell as many mm as others */ selection[i].which = pick_one(m_magic, MAXMM); selection[i].count = 1; selection[i].name = m_magic[selection[i].which].mi_name; selection[i].worth = m_magic[selection[i].which].mi_worth; switch (selection[i].which) { case MM_JUG: switch(rnd(9)) { case 0: selection[i].plus1 = P_PHASE; when 1: selection[i].plus1 = P_CLEAR; when 2: selection[i].plus1 = P_SEEINVIS; when 3: selection[i].plus1 = P_HEALING; when 4: selection[i].plus1 = P_MFIND; when 5: selection[i].plus1 = P_TFIND; when 6: selection[i].plus1 = P_HASTE; when 7: selection[i].plus1 = P_RESTORE; when 8: selection[i].plus1 = P_FLY; } when MM_OPEN: case MM_HUNGER: case MM_DRUMS: case MM_DISAPPEAR: case MM_CHOKE: case MM_KEOGHTOM: selection[i].plus1 = 3 + (rnd(3)+1) * 3; selection[i].worth += selection[i].plus1 * 50; when MM_BRACERS: selection[i].plus1 = rnd(10)+1; selection[i].worth += selection[i].plus1 * 75; when MM_DISP: selection[i].plus1 = 2; when MM_PROTECT: selection[i].plus1 = rnd(5)+1; selection[i].worth += selection[i].plus1 * 100; when MM_SKILLS: selection[i].plus1 = player.t_ctype; otherwise: break; } if(min_worth > selection[i].worth) min_worth = selection[i].worth; } break; } /* See if player can afford an item */ if (min_worth > purse) { msg("The %s eyes your small purse and departs.", monsters[NUMMONST].m_name); /* Get rid of the monster */ killed(item, FALSE, FALSE); return; } /* Display the goods */ msg("The %s shows you his wares.--More--", monsters[NUMMONST].m_name); wait_for(cw,' '); msg(""); clearok(cw, TRUE); touchwin(cw); wclear(hw); touchwin(hw); for (i=0; i < nitems; i++) { mvwaddch(hw, i+2, 0, '['); waddch(hw, (char) ((int) 'a' + i)); waddstr(hw, "] "); switch (goods) { case ARMOR: waddstr(hw, "Some "); when WEAPON: if (selection[i].count == 1) waddstr(hw, " A "); else { sprintf(buffer, "%2d ", selection[i].count); waddstr(hw, buffer); } when STICK: wprintw(hw, "A %-5s of ", ws_type[selection[i].which]); when RING: waddstr(hw, "A ring of "); when SCROLL: waddstr(hw, "A scroll of "); when POTION: waddstr(hw, "A potion of "); } if (selection[i].count > 1) sprintf(buffer, "%s%s ", selection[i].name, "s"); else sprintf(buffer, "%s ", selection[i].name); wprintw(hw, "%-24s", buffer); wprintw(hw, " Price:%5d", selection[i].worth); } sprintf(buffer, "Purse: %d", purse); mvwaddstr(hw, nitems+3, 0, buffer); mvwaddstr(hw, 0, 0, "How about one of the following goods? "); draw(hw); /* Get rid of the monster */ killed(item, FALSE, FALSE); which_item = (int) (wgetch(hw) - 'a'); while (which_item < 0 || which_item >= nitems) { if (which_item == (int) ESCAPE - (int) 'a') { return; } mvwaddstr(hw, 0, 0, "Please enter one of the listed items. "); draw(hw); which_item = (int) (wgetch(hw) - 'a'); } if (selection[which_item].worth > purse) { msg("You cannot afford it."); return; } purse -= selection[which_item].worth; item = spec_item(goods, selection[which_item].which, selection[which_item].plus1, selection[which_item].plus2); obj = OBJPTR(item); if (selection[which_item].count > 1) { obj->o_count = selection[which_item].count; obj->o_group = newgrp(); } /* If a stick or ring, let player know the type */ switch (goods) { case RING: r_know[selection[which_item].which] = TRUE; when POTION:p_know[selection[which_item].which] = TRUE; when SCROLL:s_know[selection[which_item].which] = TRUE; when STICK: ws_know[selection[which_item].which] = TRUE; when MM: m_know[selection[which_item].which] = TRUE; } if (add_pack(item, FALSE, NULL) == FALSE) { obj->o_pos = hero; fall(item, TRUE); } } /* * what to do when the hero steps next to a monster */ struct linked_list * wake_monster(int y, int x) { register struct thing *tp; register struct linked_list *it; register struct room *trp; register const char *mname; bool nasty; /* Will the monster "attack"? */ char ch; if ((it = find_mons(y, x)) == NULL) { msg("Can't find monster in show"); return (NULL); } tp = THINGPTR(it); ch = tp->t_type; trp = roomin(&tp->t_pos); /* Current room for monster */ mname = monsters[tp->t_index].m_name; /* * Let greedy ones in a room guard gold * (except in a maze where lots of creatures would all go for the * same piece of gold) */ if (on(*tp, ISGREED) && off(*tp, ISRUN) && levtype != MAZELEV && trp != NULL && lvl_obj != NULL) { register struct linked_list *item; register struct object *cur; for (item = lvl_obj; item != NULL; item = next(item)) { cur = OBJPTR(item); if ((cur->o_type == GOLD) && (roomin(&cur->o_pos) == trp)) { /* Run to the gold */ tp->t_dest = &cur->o_pos; turn_on(*tp, ISRUN); turn_off(*tp, ISDISGUISE); /* Make it worth protecting */ cur->o_count += GOLDCALC + GOLDCALC; break; } } } /* * Every time he sees mean monster, it might start chasing him */ if (on(*tp, ISMEAN) && off(*tp, ISHELD) && off(*tp, ISRUN) && rnd(100) > 33 && (!is_stealth(&player) || (on(*tp, ISUNIQUE) && rnd(100) > 95)) && (off(player, ISINVIS) || on(*tp, CANSEE)) || (trp != NULL && (trp->r_flags & ISTREAS))) { tp->t_dest = &hero; turn_on(*tp, ISRUN); turn_off(*tp, ISDISGUISE); } /* See if the monster will bother the player */ nasty = (on(*tp, ISRUN) && cansee(tp->t_pos.y, tp->t_pos.x)); /* * Let the creature summon if it can. * Also check to see if there is room around the player, * if not then the creature will wait */ if (on(*tp, CANSUMMON) && nasty && rnd(40) < tp->t_stats.s_lvl && fallpos(&hero, FALSE, 2) != NULL) { const char *helpname; int fail; register int which, i; turn_off(*tp, CANSUMMON); helpname = monsters[tp->t_index].m_typesum; for (which=1; which<NUMMONST; which++) { if (strcmp(helpname, monsters[which].m_name) == 0) break; } if (which >= NUMMONST) debug("couldn't find summoned one"); if ((off(*tp, ISINVIS) || on(player, CANSEE)) && (off(*tp, ISSHADOW) || on(player, CANSEE)) && (off(*tp, CANSURPRISE) || ISWEARING(R_ALERT))) { if (monsters[which].m_normal == FALSE) { /* genocided? */ msg("The %s appears dismayed", mname); monsters[tp->t_index].m_numsum = 0; } else { sprintf(outstring,"The %s summons %ss for help", mname, helpname); msg(outstring); } } else { if (monsters[which].m_normal == FALSE) /* genocided? */ monsters[tp->t_index].m_numsum = 0; else msg("%ss seem to appear from nowhere!", helpname); } /* * try to make all the creatures around player but remember * if unsuccessful */ for (i=0, fail=0; i<monsters[tp->t_index].m_numsum; i++) { if (!creat_mons(&player, which, FALSE)) fail++; /* remember the failures */ } /* * try once again to make the buggers */ for (i=0; i<fail; i++) creat_mons(tp, which, FALSE); /* Now let the poor fellow see all the trouble */ light(&hero); } /* * Handle monsters that can gaze and do things while running