/* File: monster.c */

/* Purpose: misc code for monsters */

/*
 * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
 *
 * This software may be copied and distributed for educational, research, and
 * not for profit purposes provided that this copyright and statement are
 * included in all such copies.
 */

#include "angband.h"



static bool spawning = FALSE;

/* Get the cave pointer */
cave_type *monster_type::get_c_ptr(void)
{
    return &cave[get_fy()][get_fx()];
}

/* Get the monster race pointer */
monster_race *monster_type::get_r_ptr(void)
{
    return &r_info[get_r_idx()];
}


/*
 * Delete a monster by index.
 *
 * This function causes the given monster to cease to exist for
 * all intents and purposes.  The monster record is left in place
 * but the record is wiped, marking it as "dead" (no race index)
 * so that it can be "skipped" when scanning the monster array,
 * and "excised" from the "m_fast" array when needed.
 *
 * Thus, anyone who makes direct reference to the "m_list[]" array
 * using monster indexes that may have become invalid should be sure
 * to verify that the "r_idx" field is non-zero.  All references
 * to "m_list[c_ptr->m_idx]" are guaranteed to be valid, see below.
 */
void delete_monster_idx(int i)
{
    int x, y;

    monster_type *m_ptr = &m_list[i];
    monster_race *r_ptr = m_ptr->r_ptr;


    /* Get location */
    x = m_ptr->fx;
    y = m_ptr->fy;


    /* Hack -- Reduce the racial counter */
    r_ptr->cur_num--;

    /* Hack -- count the number of "reproducers" */
    if (r_ptr->flags2 & RF2_MULTIPLY) num_repro--;


    /* Hack -- remove target monster */
    if (i == target_who) target_who = 0;

    /* Hack -- remove tracked monster */
    if (i == health_who) health_track(0);


    /* Monster is gone */
    cave[y][x].m_idx = 0;


    /* Visual update */
    lite_spot(y, x);


    /* Wipe the Monster */
    WIPE(m_ptr, monster_type);
}


/*
 * Delete the monster, if any, at a given location
 */
void delete_monster(int y, int x)
{
    cave_type *c_ptr;

    /* Paranoia */
    if (!in_bounds(y,x)) return;

    /* Check the grid */
    c_ptr = &cave[y][x];

    /* Delete the monster (if any) */
    if (c_ptr->m_idx) delete_monster_idx(c_ptr->m_idx);
}


/*
 * Compact and Reorder the monster list
 *
 * This function can be very dangerous, use with caution!
 *
 * When actually "compacting" monsters, we base the saving throw
 * on a combination of monster level, distance from player, and
 * current "desperation".
 *
 * After "compacting" (if needed), we "reorder" the monsters into a more
 * compact order, and we reset the allocation info, and the "live" array.
 */
void compact_monsters(int size)
{
    int         i, num, cnt;

    int         cur_lev, cur_dis, chance;


    /* Message (only if compacting) */
    if (size) msg_print("Compacting monsters...");


    /* Compact at least 'size' objects */
    for (num = 0, cnt = 1; num < size; cnt++)
    {
	/* Get more vicious each iteration */
	cur_lev = 5 * cnt;

	/* Get closer each iteration */
	cur_dis = 5 * (20 - cnt);

	/* Check all the monsters */
	for (i = 1; i < m_max; i++) 
 {
	    monster_type *m_ptr = &m_list[i];
	    monster_race *r_ptr = m_ptr->r_ptr;

	    /* Paranoia -- skip "dead" monsters */
	    if (!m_ptr->r_idx) continue;

	    /* Hack -- High level monsters start out "immune" */
	    if (r_ptr->level > cur_lev) continue;

	    /* Ignore nearby monsters */
	    if ((cur_dis > 0) && (m_ptr->cdis < cur_dis)) continue;

	    /* Saving throw chance */
	    chance = 90;

	    /* Only compact "Quest" Monsters in emergencies */
	    if ((r_ptr->flags1 & RF1_QUESTOR) && (cnt < 1000)) chance = 100;

	    /* Try not to compact Unique Monsters */
	    if (r_ptr->flags1 & RF1_UNIQUE) chance = 99;

	    /* All monsters get a saving throw */
	    if (rand_int(100) < chance) continue;

	    /* Delete the monster */
	    delete_monster_idx(i);

	    /* Count the monster */
	    num++;
	}
    }


    /* Excise dead monsters (backwards!) */
    for (i = m_max - 1; i >= 1; i--)
    {
	/* Get the i'th monster */
	monster_type *m_ptr = &m_list[i];

	/* Skip real monsters */
	if (m_ptr->r_idx) continue;

	/* One less monster */
	m_max--;

	/* Reorder */
	if (i != m_max) 
 {
	    int nx = m_list[m_max].get_fx();
	    int ny = m_list[m_max].get_fy();

	    /* Update the cave */
	    cave[ny][nx].m_idx = i;

	    /* Hack -- Update the target */
	    if (target_who == (int)(m_max)) target_who = i;

	    /* Hack -- Update the health bar */
	    if (health_who == (int)(m_max)) health_track(i);

	    /* Structure copy */
	    m_list[i] = m_list[m_max];

	    /* Wipe the hole */
	    WIPE(&m_list[m_max], monster_type);
	}
    }

    /* Reset "m_nxt" */
    m_nxt = m_max;


    /* Reset "m_top" */
    m_top = 0;

    /* Collect "live" monsters */
    for (i = 0; i < m_max; i++)
    {
	/* Collect indexes */
	m_fast[m_top++] = i;
    }
}


/*
 * Delete/Remove all the monsters when the player leaves the level
 *
 * This is an efficient method of simulating multiple calls to the
 * "delete_monster()" function, with no visual effects.
 */
void wipe_m_list()
{
    int i;

    /* Delete all the monsters */
    for (i = m_max - 1; i >= 1; i--) 
 {
	monster_type *m_ptr = &m_list[i];
        monster_race *r_ptr = m_ptr->r_ptr;

	/* Skip dead monsters */
	if (!m_ptr->r_idx) continue;

	/* Mega-Hack -- preserve Unique's XXX XXX XXX */

	/* Hack -- Reduce the racial counter */
	r_ptr->cur_num--;

	/* Monster is gone */
	cave[m_ptr->fy][m_ptr->fx].m_idx = 0;

	/* Wipe the Monster */
	WIPE(m_ptr, monster_type);
    }

    /* Reset the monster array */
    m_nxt = m_max = 1;

    /* No live monsters */
    m_top = 0;

    /* Hack -- reset "reproducer" count */
    num_repro = 0;

    /* Hack -- no more target */
    target_who = 0;

    /* Hack -- no more tracking */
    health_track(0);
}


/*
 * Acquires and returns the index of a "free" monster.
 *
 * This routine should almost never fail, but it *can* happen.
 *
 * Note that this function must maintain the special "m_fast"
 * array of indexes of "live" monsters.
 */
s16b m_pop(void)
{
    int i, n, k;


    /* Normal allocation */
    if (m_max < MAX_M_IDX)
    {
	/* Access the next hole */
	i = m_max;

	/* Expand the array */
	m_max++;

	/* Update "m_fast" */
	m_fast[m_top++] = i;

	/* Return the index */
	return (i);
    }


    /* Check for some space */
    for (n = 1; n < MAX_M_IDX; n++) 
 {
	/* Get next space */
	i = m_nxt;

	/* Advance (and wrap) the "next" pointer */
	if (++m_nxt >= MAX_M_IDX) m_nxt = 1;

	/* Skip monsters in use */
	if (m_list[i].get_r_idx()) continue;

	/* Verify space XXX XXX */
	if (m_top >= MAX_M_IDX) continue;

	/* Verify not allocated */
	for (k = 0; k < m_top; k++)
	{
	    /* Hack -- Prevent errors */
	    if (m_fast[k] == i) i = 0;
	}

	/* Oops XXX XXX */
	if (!i) continue;

	/* Update "m_fast" */
	m_fast[m_top++] = i;

	/* Use this monster */
	return (i);
    }


    /* Warn the player */
    if (character_dungeon) msg_print("Too many monsters!");

    /* Try not to crash */
    return (0);
}





/*
 * Choose a monster race that seems "appropriate" to the given level
 *
 * This function uses the "allocation table" built in "init.c".
 *
 * Note that "town" monsters will *only* be created in the town,
 * and "normal" monsters will *never* be created in the town.
 *
 * There is a small chance (1/50) of "boosting" the given depth by
 * a small amount (up to four levels), except in the town.
 *
 * It is (slightly) more likely to acquire a monster of the given level
 * than one of a lower level.  This is done by choosing several monsters
 * appropriate to the given level and keeping the "hardest" one.
 *
 * Note that we only pick 10000 different monsters before failing.
 * This may prevent "summon unique monsters" from crashing.
 *
 * As far as I can tell, this routine will never return a monster
 * which cannot be placed in the dungeon at the current time.  But
 * if we are given an overly restrictive hook, we may not terminate.
 *
 * XXX XXX XXX Note that the "get_mon_num()" function may fail, especially
 * if given an abusive "restriction", in which case it will return zero.
 */
s16b get_mon_num(int level)
{
    int         i, p, k;

    int         r_idx;

    monster_race *r_ptr;

    /* Obtain the table */
    s16b *t_lev = alloc_race_index;
    race_entry *table = alloc_race_table;


    /* Hack -- sometimes "boost" level */
    if (level > 0)
    {
	/* Occasional "nasty" monster */
	if (rand_int(NASTY_MON) == 0)
	{
	    /* Pick a level bonus */
	    int d = level / 4 + 2;

	    /* Boost the level */
	    level += ((d < 5) ? d : 5);
	}

	/* Occasional "nasty" monster */
	if (rand_int(NASTY_MON) == 0)
	{
	    /* Pick a level bonus */
	    int d = level / 4 + 2;

	    /* Boost the level */
	    level += ((d < 5) ? d : 5);
	}

	/* Only examine legal levels */
	if (level > MAX_DEPTH - 1) level = MAX_DEPTH - 1;
    }


    /* Hack -- Pick a monster */
    for (k = 0; k < 10000; k++)
    {
	/* Town level is easy */
	if (level <= 0)
	{
	    /* Pick a level 0 entry */
	    i = rand_int(t_lev[0]);
	}

	/* Other levels */
	else
	{
	    /* Roll for rerolls */
	    p = rand_int(100);

	    /* Pick any "appropriate" monster */
	    i = rand_int(t_lev[level]);

	    /* Try for a "harder" monster twice (10%) */
	    if (p < 10)
	    {
		/* Pick another monster at or below the given level */
		int j = rand_int(t_lev[level]);

		/* Keep it if it is "better" */
		if (table[i].locale < table[j].locale) i = j;
	    }

	    /* Try for a "harder" monster once (50%) */
	    if (p < 60)
	    {
		/* Pick another monster at or below the given level */
		int j = rand_int(t_lev[level]);

		/* Keep it if it is "better" */
		if (table[i].locale < table[j].locale) i = j;
	    }

	    /* Hack -- Never make town monsters */
	    if (table[i].locale == 0) continue;
	}


	/* Access the "r_idx" of the chosen monster */
	r_idx = table[i].r_idx;


	/* Hack -- apply the hook (if needed) */
	if (get_mon_num_hook && (!(*get_mon_num_hook)(r_idx))) continue;


	/* Access the actual race */
	r_ptr = &r_info[r_idx];


	/* Hack -- "unique" monsters must be "unique" */
	if ((r_ptr->flags1 & RF1_UNIQUE) &&
	    (r_ptr->cur_num >= r_ptr->max_num))
	{
	    continue;
	}


	/* Depth Monsters never appear out of depth */
	if ((r_ptr->flags1 & RF1_FORCE_DEPTH) && (r_ptr->level > dun_level))
	{
	    continue;
	}


	/* Hack -- Roll for "rarity" */
	if (rand_int(table[i].chance) != 0) continue;


	/* Use that monster */
	return (r_idx);
    }


    /* Oops */
    return (0);
}





/*
 * Build a string describing a monster in some way.
 *
 * We can correctly describe monsters based on their visibility.
 * We can force all monsters to be treated as visible or invisible.
 * We can build nominatives, objectives, possessives, or reflexives.
 * We can selectively pronominalize hidden, visible, or all monsters.
 * We can use definite or indefinite descriptions for hidden monsters.
 * We can use definite or indefinite descriptions for visible monsters.
 *
 * Pronominalization involves the gender whenever possible and allowed,
 * so that by cleverly requesting pronominalization / visibility, you
 * can get messages like "You hit someone.  She screams in agony!".
 *
 * Reflexives are acquired by requesting Objective plus Possessive.
 *
 * If no m_ptr arg is given (?), the monster is assumed to be hidden,
 * unless the "Assume Visible" mode is requested.
 *
 * If no r_ptr arg is given, it is extracted from m_ptr and r_info
 * If neither m_ptr nor r_ptr is given, the monster is assumed to
 * be neuter, singular, and hidden (unless "Assume Visible" is set),
 * in which case you may be in trouble... :-)
 *
 * I am assuming that no monster name is more than 70 characters long,
 * so that "char desc[80];" is sufficiently large for any result.
 *
 * Mode Flags:
 *   0x01 --> Objective (or Reflexive)
 *   0x02 --> Possessive (or Reflexive)
 *   0x04 --> Use indefinites for hidden monsters ("something")
 *   0x08 --> Use indefinites for visible monsters ("a kobold")
 *   0x10 --> Pronominalize hidden monsters
 *   0x20 --> Pronominalize visible monsters
 *   0x40 --> Assume the monster is hidden
 *   0x80 --> Assume the monster is visible
 *
 * Useful Modes:
 *   0x00 --> Full nominative name ("the kobold") or "it"
 *   0x04 --> Full nominative name ("the kobold") or "something"
 *   0x80 --> Genocide resistance name ("the kobold")
 *   0x88 --> Killing name ("a kobold")
 *   0x22 --> Possessive, genderized if visable ("his") or "its"
 *   0x23 --> Reflexive, genderized if visable ("himself") or "itself"
 */
void monster_desc(char *desc, monster_type *m_ptr, int mode)
{
    cptr res;
    monster_race *r_ptr = m_ptr->r_ptr;
    cptr name = (r_name + r_ptr->name);
    bool seen, pron;


    /* Can we "see" it (exists + forced, or visible + not unforced) */
    seen = (m_ptr && ((mode & 0x80) || (!(mode & 0x40) && m_ptr->ml)));

    /* Sexed Pronouns (seen and allowed, or unseen and allowed) */
    pron = (m_ptr && ((seen && (mode & 0x20)) || (!seen && (mode & 0x10))));


    /* First, try using pronouns, or describing hidden monsters */
    if (!seen || pron)
    {
	/* an encoding of the monster "sex" */
	int kind = 0x00;

	/* Extract the gender (if applicable) */
	if (r_ptr->flags1 & RF1_FEMALE) kind = 0x20;
	else if (r_ptr->flags1 & RF1_MALE) kind = 0x10;

	/* Ignore the gender (if desired) */
	if (!m_ptr || !pron) kind = 0x00;


	/* Assume simple result */
	res = "it";

	/* Brute force: split on the possibilities */
	switch (kind + (mode & 0x07))
	{
	    /* Neuter, or unknown */
	    case 0x00: res = "it"; break;
	    case 0x01: res = "it"; break;
	    case 0x02: res = "its"; break;
	    case 0x03: res = "itself"; break;
	    case 0x04: res = "something"; break;
	    case 0x05: res = "something"; break;
	    case 0x06: res = "something's"; break;
	    case 0x07: res = "itself"; break;

	    /* Male (assume human if vague) */
	    case 0x10: res = "he"; break;
	    case 0x11: res = "him"; break;
	    case 0x12: res = "his"; break;
	    case 0x13: res = "himself"; break;
	    case 0x14: res = "someone"; break;
	    case 0x15: res = "someone"; break;
	    case 0x16: res = "someone's"; break;
	    case 0x17: res = "himself"; break;

	    /* Female (assume human if vague) */
	    case 0x20: res = "she"; break;
	    case 0x21: res = "her"; break;
	    case 0x22: res = "her"; break;
	    case 0x23: res = "herself"; break;
	    case 0x24: res = "someone"; break;
	    case 0x25: res = "someone"; break;
	    case 0x26: res = "someone's"; break;
	    case 0x27: res = "herself"; break;
	}

	/* Copy the result */
	(void)strcpy(desc, res);
    }


    /* Handle visible monsters, "reflexive" request */
    else if ((mode & 0x02) && (mode & 0x01))
    {
	/* The monster is visible, so use its gender */
	if (r_ptr->flags1 & RF1_FEMALE) strcpy(desc, "herself");
	else if (r_ptr->flags1 & RF1_MALE) strcpy(desc, "himself");
	else strcpy(desc, "itself");
    }


    /* Handle all other visible monster requests */
    else
    {
	/* It could be a Unique */
	if (r_ptr->flags1 & RF1_UNIQUE)
	{
	    /* Start with the name (thus nominative and objective) */
	    (void)strcpy(desc, name);
	}

	/* It could be an indefinite monster */
	else if (mode & 0x08)
	{
	    /* XXX Check plurality for "some" */

	    /* Indefinite monsters need an indefinite article */
	    (void)strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ");
	    (void)strcat(desc, name);
	}

	/* It could be a normal, definite, monster */
	else 
 {
	    /* Definite monsters need a definite article */
	    strcpy(desc, "the ");
	    strcat(desc, name);
	}

	/* Handle the Possessive as a special afterthought */
	if (mode & 0x02) 
 {
            /* XXX Check for trailing "s" */

	    /* Simply append "apostrophe" and "s" */
	    strcat(desc, "'s");
	}
    }
}




/*
 * Learn about a monster (by "probing" it)
 */
void lore_do_probe(int m_idx)
{
    monster_type *m_ptr = &m_list[m_idx];
    monster_race *r_ptr = m_ptr->r_ptr;

    /* Hack -- Memorize some flags */
    r_ptr->r_flags1 = r_ptr->flags1;
    r_ptr->r_flags2 = r_ptr->flags2;
    r_ptr->r_flags3 = r_ptr->flags3;

    /* Redraw the recall window */
    p_ptr->redraw |= PR_RECENT;
}


/*
 * Take note that the given monster just dropped some treasure
 *
 * Note that learning the "GOOD"/"GREAT" flags gives information
 * about the treasure (even when the monster is killed for the first
 * time, such as uniques, and the treasure has not been examined yet).
 *
 * This "indirect" method is used to prevent the player from learning
 * exactly how much treasure a monster can drop from observing only
 * a single example of a drop.  This method actually observes how much
 * gold and items are dropped, and remembers that information to be
 * described later by the monster recall code.
 */
void lore_treasure(int m_idx, int num_item, int num_gold)
{
    monster_type *m_ptr = &m_list[m_idx];
    monster_race *r_ptr = m_ptr->r_ptr;

    /* Note the number of things dropped */
    if (num_item > r_ptr->r_drop_item) r_ptr->r_drop_item = num_item;
    if (num_gold > r_ptr->r_drop_gold) r_ptr->r_drop_gold = num_gold;

    /* Hack -- memorize the good/great flags */
    if (r_ptr->flags1 & RF1_DROP_GOOD) r_ptr->r_flags1 |= RF1_DROP_GOOD;
    if (r_ptr->flags1 & RF1_DROP_GREAT) r_ptr->r_flags1 |= RF1_DROP_GREAT;
}



/*
 * This function updates the monster record of the given monster
 *
 * This involves extracting the distance to the player, checking
 * for visibility (natural, infravision, see-invis, telepathy),
 * updating the monster visibility flag, redrawing or erasing the
 * monster when the visibility changes, and taking note of any
 * "visual" features of the monster (cold-blooded, invisible, etc).
 *
 * The only monster fields that are changed here are "cdis" (the
 * distance from the player), "los" (clearly visible to player),
 * and "ml" (visible to the player in any way).
 *
 * There are a few cases where the calling routine knows that the
 * distance from the player to the monster has not changed, and so
 * we have a special parameter to request distance computation.
 * This lets many calls to this function run very quickly.
 *
 * Note that every time a monster moves, we must call this function
 * for that monster, and update distance.  Note that every time the
 * player moves, we must call this function for every monster, and
 * update distance.  Note that every time the player "state" changes
 * in certain ways (including "blindness", "infravision", "telepathy",
 * and "see invisible"), we must call this function for every monster.
 *
 * The routines that actually move the monsters call this routine
 * directly, and the ones that move the player, or notice changes
 * in the player state, call "update_monsters()".
 *
 * Routines that change the "illumination" of grids must also call
 * this function, since the "visibility" of some monsters may be
 * based on the illumination of their grid.
 *
 * Note that this function is called once per monster every time the
 * player moves, so it is important to optimize it for monsters which
 * are far away.  Note the optimization which skips monsters which
 * are far away and were completely invisible last turn.
 *
 * Note the optimized "inline" version of the "distance()" function.
 *
 * Note that only monsters on the current panel can be "visible",
 * and then only if they are (1) in line of sight and illuminated
 * by light or infravision, or (2) nearby and detected by telepathy.
 *
 * The player can choose to be disturbed by several things, including
 * "disturb_enter" (monster becomes "easily" viewable), "disturb_leave"
 * (monster becomes no longer "easily" viewable), "disturb_move" (monster
 * which is "easily" viewable moves), and "disturb_near" (monster which
 * is "easily" viewable, and within 5 grids of the player, moves).  This
 * may or may not be the "best" way to handle disturbance and telepathy,
 * but it should be better than the old method.
 */
void update_mon(int m_idx, bool dist)
{
    monster_type *m_ptr = &m_list[m_idx];
    monster_race *r_ptr = m_ptr->r_ptr;

    /*  The current monster location */
    int fx = m_ptr->fx;
    int fy = m_ptr->fy;

    /*  Seen at all */
    bool flag = FALSE;

    /*  Seen by vision */
    bool easy = FALSE;

    /*  Seen by telepathy */
    bool hard = FALSE;

    /*  Various extra flags */
    bool do_empty_mind = FALSE;
    bool do_weird_mind = FALSE;
    bool do_invisible = FALSE;
    bool do_cold_blood = FALSE;


    /* Calculate distance */
    if (dist) 
 {
	int d, dy, dx;

	/* Distance components */
	dy = (py > fy) ? (py - fy) : (fy - py);
	dx = (px > fx) ? (px - fx) : (fx - px);

	/* Approximate distance */
	d = (dy > dx) ? (dy + (dx>>1)) : (dx + (dy>>1));

	/* Save the distance (in a byte) */
	m_ptr->set_cdis((d < 255) ? d : 255);
    }


    /* Process "distant" monsters */
    if (m_ptr->cdis > MAX_SIGHT) 
 {
	/* Ignore unseen monsters */
	if (!m_ptr->ml) return;
    }

    /* Process "nearby" monsters on the current "panel" */
    else if (panel_contains(fy, fx)) 
 {
	cave_type *c_ptr = &cave[fy][fx];

	/* Normal line of sight, and player is not blind */
	if ((c_ptr->info & CAVE_VIEW) && (!p_ptr->blind)) 
 {
	    /* Use "infravision" */
	    if (m_ptr->cdis <= (byte)(p_ptr->see_infra)) 
 {
		/* Infravision only works on "warm" creatures */
		/* Below, we will need to know that infravision failed */
		if (r_ptr->flags2 & RF2_COLD_BLOOD) do_cold_blood = TRUE;

		/* Infravision works */
		if (!do_cold_blood) easy = flag = TRUE;
	    }

	    /* Use "illumination" */
	    if (c_ptr->info & (CAVE_LITE | CAVE_GLOW)) 
 {
		/* Take note of invisibility */
		if (r_ptr->flags2 & RF2_INVISIBLE) do_invisible = TRUE;

		/* Visible, or detectable, monsters get seen */
		if (!do_invisible || p_ptr->see_inv) easy = flag = TRUE;
	    }
	}

	/* Telepathy can see all "nearby" monsters with "minds" */
	if (p_ptr->telepathy) 
 {
	    /* Empty mind, no telepathy */
	    if (r_ptr->flags2 & RF2_EMPTY_MIND) 
 {
		do_empty_mind = TRUE;
	    }

	    /* Weird mind, occasional telepathy */
	    else if (r_ptr->flags2 & RF2_WEIRD_MIND) 
 {
		do_weird_mind = TRUE;
		if (rand_int(100) < 10) hard = flag = TRUE;
	    }

	    /* Normal mind, allow telepathy */
	    else 
 {
		hard = flag = TRUE;
	    }

	    /* Apply telepathy */
	    if (hard) 
 {
		/* Hack -- Memorize mental flags */
		if (r_ptr->flags2 & RF2_SMART) r_ptr->r_flags2 |= RF2_SMART;
		if (r_ptr->flags2 & RF2_STUPID) r_ptr->r_flags2 |= RF2_STUPID;
	    }
	}

	/* Hack -- Wizards have "perfect telepathy" */
	if (wizard) flag = TRUE;
    }


    /* The monster is now visible */
    if (flag)
    {
	/* It was previously unseen */
	if (!m_ptr->ml) 
 {
	    /* Mark as visible */
	    m_ptr->set_ml(TRUE);

	    /* Draw the monster */
	    lite_spot(fy, fx);

	    /* Update health bar as needed */
	    if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);

	    /* Hack -- Count "fresh" sightings */
	    if (r_ptr->r_sights < MAX_SHORT) r_ptr->r_sights++;
	}

	/* Memorize various observable flags */
	if (do_empty_mind) r_ptr->r_flags2 |= RF2_EMPTY_MIND;
	if (do_weird_mind) r_ptr->r_flags2 |= RF2_WEIRD_MIND;
	if (do_cold_blood) r_ptr->r_flags2 |= RF2_COLD_BLOOD;
	if (do_invisible) r_ptr->r_flags2 |= RF2_INVISIBLE;

	/* Efficiency -- Notice multi-hued monsters */
	if (r_ptr->flags1 & RF1_ATTR_MULTI) scan_monsters = TRUE;
    }

    /* The monster is not visible */
    else 
 {
	/* It was previously seen */
	if (m_ptr->ml) 
 {
	    /* Mark as not visible */
	    m_ptr->set_ml(FALSE);

	    /* Erase the monster */
	    lite_spot(fy, fx);

	    /* Update health bar as needed */
	    if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH);
	}
    }


    /* The monster is now easily visible */
    if (easy)
    {
	/* Change */
	if (!m_ptr->los) 
 {
	    /* Mark as easily visible */
	    m_ptr->set_los(TRUE);

	    /* Disturb on appearance */
	    if (disturb_enter) disturb(1, 0);
	}
    }

    /* The monster is not easily visible */
    else
    {
	/* Change */
	if (m_ptr->los) 
 {
	    /* Mark as not easily visible */
	    m_ptr->set_los(FALSE);

	    /* Disturb on disappearance */
	    if (disturb_leave) disturb(1, 0);
	}
    }
}




/*
 * This function simply updates all the (non-dead) monsters (see above).
 */
void update_monsters(bool dist)
{
    int          i;

    /* Update each (live) monster */
    for (i = 1; i < m_max; i++)
    {
	monster_type *m_ptr = &m_list[i];

	/* Skip dead monsters */
	if (!m_ptr->r_idx) continue;

	/* Update the monster */
	update_mon(i, dist);
    }
}




/*
 * Attempt to place a monster of the given race at the given location.
 *
 * To give the player a sporting chance, any monster that appears in
 * line-of-sight and is extremely dangerous can be marked as
 * "FORCE_SLEEP", which will cause them to be placed with low energy,
 * which often (but not always) lets the player move before they do.
 *
 * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
 *
 * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
 * remove old "cur_num" and "max_num" fields.
 *
 * XXX XXX XXX Actually, do something similar for artifacts, to simplify
 * the "preserve" mode, and to make the "what artifacts" flag more useful.
 */
static bool place_monster_one(int y, int x, int r_idx, bool slp)
{
    int i;
    cave_type *c_ptr;
    monster_type *m_ptr;
    monster_race *r_ptr = &r_info[r_idx];
    cptr name = (r_name + r_ptr->name);


    /* Verify location */
    if (!in_bounds(y,x)) return (FALSE);

    /* Require empty space */
    if (!empty_grid_bold(y, x)) return (FALSE);

    /* Hack -- no creation on glyph of warding */
    if (cave[y][x].feat == CF_GLYPH) return (FALSE);


    /* Paranoia */
    if (!r_idx) return (FALSE);

    /* Paranoia */
    if (!r_ptr->name) return (FALSE);


    /* Hack -- "unique" monsters must be "unique" */
    if ((r_ptr->flags1 & RF1_UNIQUE) && (r_ptr->cur_num >= r_ptr->max_num)) 
 {
	/* Cannot create */
	return FALSE;
    }


    /* Depth monsters may NOT be created out of depth */
    if ((r_ptr->flags1 & RF1_FORCE_DEPTH) && (dun_level < r_ptr->level)) 
 {
	/* Cannot create */
	return FALSE;
    }


    /* Powerful monster */
    if (r_ptr->level > dun_level) 
 {
	/* Unique monsters */
	if (r_ptr->flags1 & RF1_UNIQUE) 
 {
	    /* Message for cheaters */
	    if (cheat_hear) msg_format("Deep Unique (%s).", name);

	    /* Boost rating by twice delta-depth and a bit more */
	    rating += (r_ptr->level - dun_level) * 2 + 5;
	}

	/* Normal monsters */
	else 
 {
	    /* Message for cheaters */
	    if (cheat_hear) msg_format("Deep Monster (%s).", name);

	    /* Boost rating by delta-depth */
	    rating += (r_ptr->level - dun_level);
	}
    }

    /* Note the monster */
    else if (r_ptr->flags1 & RF1_UNIQUE) 
 {
	/* Unique monsters induce message */
	if (cheat_hear) msg_format("Unique (%s).", name);

        /* Boost rating slightly */
        rating += 5;
    }


    /* Access the location */
    c_ptr = &cave[y][x];

    /* Make a new monster */
    c_ptr->m_idx = m_pop();

    /* Mega-Hack -- catch "failure" */
    if (!c_ptr->m_idx) return (FALSE);


    /* Get a new monster record */
    m_ptr = &m_list[c_ptr->m_idx];

    /* Save the race */
    m_ptr->set_r_idx(r_idx);

    /* Place the monster at the location */
    m_ptr->set_location(x, y);


    /* Hack -- Count the monsters on the level */
    r_ptr->cur_num++;


    /* Hack -- count the number of "reproducers" */
    if (r_ptr->flags2 & RF2_MULTIPLY) num_repro++;


    /* Assign maximal hitpoints */
    if (r_ptr->flags1 & RF1_FORCE_MAXHP) 
 {
	m_ptr->set_maxhp(maxroll(r_ptr->hdice, r_ptr->hside));
    }
    else 
 {
	m_ptr->set_maxhp(damroll(r_ptr->hdice, r_ptr->hside));
    }

    /* And start out fully healthy */
    m_ptr->set_hp(m_ptr->maxhp);


    /* Extract the monster base speed */
    m_ptr->set_mspeed(r_ptr->speed);

    /* Hack -- small racial variety */
    if (!(r_ptr->flags1 & RF1_UNIQUE)) 
 {
	/* Allow some small variation per monster */
	i = extract_energy[r_ptr->speed] / 10;
	if (i) m_ptr->set_mspeed(m_ptr->mspeed + rand_spread(0, i));
    }


    /* Hack -- Reduce risk of "instant death by breath weapons" */
    if (r_ptr->flags1 & RF1_FORCE_SLEEP) 
 {
	/* Start out with minimal energy */
	m_ptr->set_energy(rand_int(10));
    }

    /* Give a random starting energy */
    else 
 {
        m_ptr->set_energy(rand_int(100));
    }


    /* No "damage" yet */
    m_ptr->set_stunned(0);
    m_ptr->set_confused(0);
    m_ptr->set_monfear(0);
    m_ptr->set_temp_speed(0);
    m_ptr->set_temp_slow(0);

    /* No knowledge */
    m_ptr->set_cdis(0);
    m_ptr->set_los(FALSE);
    m_ptr->set_ml(FALSE);

    /* Was it spawned? */
    m_ptr->set_spawned(spawning);

    /* Update the monster */
    update_mon(c_ptr->m_idx, TRUE);


    /* Assume no sleeping */
    m_ptr->set_csleep(0);

    /* Enforce sleeping if needed */
    if (slp && r_ptr->sleep) 
 {
	int val = r_ptr->sleep;
	m_ptr->set_csleep((val * 2) + randint(val * 10));
    }


    /* Success */
    return TRUE;
}


/*
 * Maximum size of a group of monsters
 */
#define GROUP_MAX       32


/*
 * Attempt to place a "group" of monsters around the given location
 */
static bool place_monster_group(int y, int x, int r_idx, bool slp)
{
    monster_race *r_ptr = &r_info[r_idx];

    int old, n, i;
    int total = 0, extra = 0;

    int hack_n = 0;

    byte hack_y[GROUP_MAX];
    byte hack_x[GROUP_MAX];


    /* Pick a group size */
    total = randint(13);

    /* Hard monsters, small groups */
    if (r_ptr->level > dun_level)
    {
	extra = r_ptr->level - dun_level;
	extra = 0 - randint(extra);
    }

    /* Easy monsters, large groups */
    else if (r_ptr->level < dun_level)
    {
	extra = dun_level - r_ptr->level;
	extra = randint(extra);
    }

    /* Hack -- limit group reduction */
    if (extra > 12) extra = 12;

    /* Modify the group size */
    total += extra;

    /* Minimum size */
    if (total < 1) total = 1;

    /* Maximum size */
    if (total > GROUP_MAX) total = GROUP_MAX;


    /* Save the rating */
    old = rating;

    /* Start on the monster */
    hack_n = 1;
    hack_x[0] = x;
    hack_y[0] = y;

    /* Puddle monsters, breadth first, up to total */
    for (n = 0; (n < hack_n) && (hack_n < total); n++)
    {
	/* Grab the location */
	int hx = hack_x[n];
	int hy = hack_y[n];

	/* Check each direction, up to total */
	for (i = 0; (i < 8) && (hack_n < total); i++)
	{
	    int mx = hx + ddx_ddd[i];
	    int my = hy + ddy_ddd[i];

	    /* Walls and Monsters block flow */
	    if (!empty_grid_bold(my, mx)) continue;

	    /* Attempt to place another monster */
	    if (place_monster_one(my, mx, r_idx, slp))
	    {
		/* Add it to the "hack" set */
		hack_y[hack_n] = my;
		hack_x[hack_n] = mx;
		hack_n++;
	    }
	}
    }

    /* Hack -- restore the rating */
    rating = old;


    /* Success */
    return (TRUE);
}


/*
 * Hack -- help pick an escort type
 */
static int place_monster_idx = 0;

/*
 * Hack -- help pick an escort type
 */
static bool place_monster_okay(int r_idx)
{
    monster_race *r_ptr = &r_info[place_monster_idx];

    monster_race *z_ptr = &r_info[r_idx];

    /* Require similar "race" */
    if (z_ptr->r_char != r_ptr->r_char) return (FALSE);

    /* Skip more advanced monsters */
    if (z_ptr->level > r_ptr->level) return (FALSE);

    /* Skip unique monsters */
    if (z_ptr->flags1 & RF1_UNIQUE) return (FALSE);

    /* Paranoia -- Skip identical monsters */
    if (place_monster_idx == r_idx) return (FALSE);

    /* Okay */
    return (TRUE);
}


/*
 * Attempt to place a monster of the given race at the given location
 *
 * Note that certain monsters are now marked as requiring "friends".
 * These monsters, if successfully placed, and if the "grp" parameter
 * is TRUE, will be surrounded by a "group" of identical monsters.
 *
 * Note that certain monsters are now marked as requiring an "escort",
 * which is a collection of monsters with similar "race" but lower level.
 *
 * Some monsters induce a fake "group" flag on their escorts.
 *
 * Note the "bizarre" use of non-recursion to prevent annoying output
 * when running a code profiler.
 */
bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp)
{
    int                 i;

    monster_race        *r_ptr = &r_info[r_idx];


    /* Place one monster, or fail */
    if (!place_monster_one(y, x, r_idx, slp)) return (FALSE);


    /* Require the "group" flag */
    if (!grp) return (TRUE);


    /* Friends for certain monsters */
    if (r_ptr->flags1 & RF1_FRIENDS) 
 {
	/* Attempt to place a group */
	(void)place_monster_group(y, x, r_idx, slp);
    }


    /* Escorts for certain monsters */
    if (r_ptr->flags1 & RF1_ESCORT)
    {
	/* Try to place several "escorts" */
	for (i = 0; i < 50; i++)
	{
	    int nx, ny, z, d = 3;

	    /* Pick a location */
	    scatter(&ny, &nx, y, x, d, 0);

	    /* Require empty grids */
	    if (!empty_grid_bold(ny, nx)) continue;

	    /* Set the escort index */
	    place_monster_idx = r_idx;

	    /* Set the escort hook */
	    get_mon_num_hook = place_monster_okay;

	    /* Pick a random race */
	    z = get_mon_num(r_ptr->level);

	    /* Remove restriction */
	    get_mon_num_hook = NULL;

	    /* Handle failure */
	    if (!z) break;

	    /* Place a single escort */
	    (void)place_monster_one(ny, nx, z, slp);

	    /* Place a "group" of escorts if needed */
	    if ((r_info[z].flags1 & RF1_FRIENDS) ||
		(r_ptr->flags1 & RF1_ESCORTS))
	    {
		/* Place a group of monsters */
		(void)place_monster_group(ny, nx, z, slp);
	    }
	}
    }


    /* Success */
    return (TRUE);
}


/*
 * Hack -- attempt to place a monster at the given location
 *
 * Attempt to find a monster appropriate to the "monster_level"
 */
bool place_monster(int y, int x, bool slp, bool grp)
{
    int r_idx;

    /* Pick a monster */
    r_idx = get_mon_num(monster_level);

    /* Handle failure */
    if (!r_idx) return (FALSE);

    /* Attempt to place the monster */
    if (place_monster_aux(y, x, r_idx, slp, grp)) return (TRUE);

    /* Oops */
    return (FALSE);
}




/*
 * XXX XXX XXX Player Ghosts are such a hack, they have been completely
 * removed until Angband 2.8.0, in which there will actually be a small
 * number of "unique" monsters which will serve as the "player ghosts".
 * Each will have a place holder for the "name" of a deceased player,
 * which will be extracted from a "bone" file, or replaced with a
 * "default" name if a real name is not available.  Each ghost will
 * appear exactly once and will not induce a special feeling.
 *
 * Possible methods:
 *   (s) 1 Skeleton
 *   (z) 1 Zombie
 *   (M) 1 Mummy
 *   (G) 1 Polterguiest, 1 Spirit, 1 Ghost, 1 Shadow, 1 Phantom
 *   (W) 1 Wraith
 *   (V) 1 Vampire, 1 Vampire Lord
 *   (L) 1 Lich
 *
 * Possible change: Lose 1 ghost, Add "Master Lich"
 *
 * Possible change: Lose 2 ghosts, Add "Wraith", Add "Master Lich"
 *
 * Possible change: Lose 4 ghosts, lose 1 vampire lord
 *
 * Note that ghosts should never sleep, should be very attentive, should
 * have maximal hitpoints, drop only good (or great) items, should be
 * cold blooded, evil, undead, immune to poison, sleep, confusion, fear.
 *
 * Base monsters:
 *   Skeleton
 *   Zombie
 *   Mummy
 *   Poltergeist
 *   Spirit
 *   Ghost
 *   Vampire
 *   Wraith
 *   Vampire Lord
 *   Shadow
 *   Phantom
 *   Lich
 *
 * This routine will simply extract ghost names from files, and
 * attempt to allocate a player ghost somewhere in the dungeon,
 * note that normal allocation may also attempt to place ghosts,
 * so we must work with some form of default names.
 *
 * XXX XXX XXX XXX
 */





/*
 * Attempt to allocate a random monster in the dungeon.
 * Place the monster at least "dis" distance from the player.
 * Use "slp" to choose the initial "sleep" status
 * Use "monster_level" for the monster level
 */
bool alloc_monster(int dis, int slp)
{
    int                 y, x;

    /* Find a legal, distant, unoccupied, space */
    while (1)
    {
	/* Pick a location */
	y = rand_int(cur_hgt);
	x = rand_int(cur_wid);

	/* Require "naked" floor grid */
	if (!naked_grid_bold(y,x)) continue;

	/* Accept far away grids */
	if (distance(y, x, py, px) > dis) break;
    }

    /* Attempt to place the monster, allow groups */
    if (place_monster(y, x, slp, TRUE)) return (TRUE);

    /* Nope */
    return (FALSE);
}




/*
 * Hack -- the "type" of the current "summon specific"
 */
static int summon_specific_type = 0;


/*
 * Hack -- help decide if a monster race is "okay" to summon
 */
static bool summon_specific_okay(int r_idx)
{
    monster_race *r_ptr = &r_info[r_idx];

    bool okay = FALSE;


    /* Hack -- no specific type specified */
    if (!summon_specific_type) return (TRUE);


    /* Check our requirements */
    switch (summon_specific_type)
    {
	case SUMMON_ANT:
	    okay = ((r_ptr->r_char == 'a') &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_SPIDER:
	    okay = ((r_ptr->r_char == 'S') &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_HOUND:
	    okay = (((r_ptr->r_char == 'C') || (r_ptr->r_char == 'Z')) &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_HYDRA:
	    okay = ((r_ptr->r_char == 'M') &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_ANGEL:
	    okay = ((r_ptr->r_char == 'A') &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_DEMON:
	    okay = ((r_ptr->flags3 & RF3_DEMON) &&
		!(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_UNDEAD:
	    okay = ((r_ptr->flags3 & RF3_UNDEAD) &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_DRAGON:
	    okay = ((r_ptr->flags3 & RF3_DRAGON) &&
		    !(r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_HI_UNDEAD:
	    okay = ((r_ptr->r_char == 'L') ||
		    (r_ptr->r_char == 'V') ||
		    (r_ptr->r_char == 'W'));
	    break;

	case SUMMON_HI_DRAGON:
	    okay = (r_ptr->r_char == 'D');
	    break;

	case SUMMON_WRAITH:
	    okay = ((r_ptr->r_char == 'W') &&
		    (r_ptr->flags1 & RF1_UNIQUE));
	    break;

	case SUMMON_UNIQUE:
	    okay = (r_ptr->flags1 & RF1_UNIQUE);
	    break;
    }

    /* Result */
    return (okay);
}


/*
 * Place a monster (of the specified "type") near the given
 * location.  Return TRUE iff a monster was actually summoned.
 *
 * We will attempt to place the monster up to 10 times before giving up.
 *
 * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's
 * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's
 * Note: None of the other summon codes will ever summon Unique's.
 *
 * This function has been changed.  We now take the "monster level"
 * of the summoning monster as a parameter, and use that, along with
 * the current dungeon level, to help determine the level
 * of the desired monster.  Note that this is an upper bound, and
 * also tends to "prefer" monsters of that level.
 *
 * Currently, we use the average of the dungeon and monster levels,
 * and then add five to allow slight increases in monster power.
 *
 * Note that this function may not succeed, expecially when summoning
 * unique monsters, or when the "legal" summon group is very small.
 *
 * This would be another place where using a "temporary" monster
 * allocation table would be a useful optimization.  XXX XXX XXX
 */
bool summon_specific(int y1, int x1, int lev, int type)
{
    int i, x, y, r_idx;

    bool result = FALSE;

    /* Try to place it */
    for (i = 0; i < 20; ++i)
    {
	/* Pick a distance */
	int d = (i / 15) + 1;

	/* Pick a location */
	scatter(&y, &x, y1, x1, d, 0);

	/* Require "empty" floor grid */
	if (!empty_grid_bold(y, x)) continue;

	/* Hack -- no summon on glyph of warding */
	if (cave[y][x].feat == CF_GLYPH) continue;

	/* Save the "summon" type */
	summon_specific_type = type;

	/* Require "okay" monsters */
	get_mon_num_hook = summon_specific_okay;

	/* Choose a monster level */
	monster_level = (dun_level + lev) / 2 + 5;

	/* Pick a monster */
	r_idx = get_mon_num(monster_level);

	/* Restore monster level */
	monster_level = dun_level;

	/* Remove restriction */
	get_mon_num_hook = NULL;

	/* Forget "summon" type */
	summon_specific_type = 0;

	/* Handle failure */
	if (!r_idx) return (FALSE);

	/* Attempt to place the monster (awake, allow groups) */
	result = place_monster_aux(y, x, r_idx, FALSE, TRUE);

	/* Done */
	break;
    }

    /* Failure */
    return (result);
}





/*
 * Let the given monster attempt to reproduce.
 *
 * Note that "reproduction" REQUIRES empty space.
 *
 * Also used for cloning.
 */
bool multiply_monster(int m_idx)
{
    monster_type *m_ptr = &m_list[m_idx];
    int i, y, x;
    bool result = FALSE;

    /* Make the monsters marked as spawned */
    spawning = TRUE;

    /* Try up to 18 times */
    for (i = 0; i < 18; i++) 
 {
	int d = 1;

	/* Pick a location */
	scatter(&y, &x, m_ptr->fy, m_ptr->fx, d, 0);

	/* Require an "empty" floor grid */
	if (!empty_grid_bold(y, x)) continue;

	/* Create a new monster (awake, no groups) */
	result = place_monster_aux(y, x, m_ptr->r_idx, FALSE, FALSE);

	/* Done */
	break;
    }

    /* Future monsters not spawned */
    spawning = FALSE;

    /* Result */
    return result;
}





/*
 * Dump a message describing a monster's reaction to damage
 *
 * Technically should attempt to treat "Beholder"'s as jelly's
 */
void message_pain(int m_idx, int dam)
{
    long oldhp, newhp, tmp;
    int percentage;

    monster_type *m_ptr = &m_list[m_idx];
    monster_race *r_ptr = m_ptr->r_ptr;

    char m_name[80];


    /* Get the monster name */
    monster_desc(m_name, m_ptr, 0);

    /* Notice non-damage */
    if (dam == 0) 
 {
	msg_format("%^s is unharmed.", m_name);
	return;
    }

    /* Note -- subtle fix -CFT */
    newhp = (long)(m_ptr->hp);
    oldhp = newhp + (long)(dam);
    tmp = (newhp * 100L) / oldhp;
    percentage = (int)(tmp);


    /* Jelly's, Mold's, Vortex's, Quthl's */
    if (strchr("jmvQ", r_ptr->r_char)) 
 {
	if (percentage > 95)
	    msg_format("%^s barely notices.", m_name);
	else if (percentage > 75)
	    msg_format("%^s flinches.", m_name);
	else if (percentage > 50)
	    msg_format("%^s squelches.", m_name);
	else if (percentage > 35)
	    msg_format("%^s quivers in pain.", m_name);
	else if (percentage > 20)
	    msg_format("%^s writhes about.", m_name);
	else if (percentage > 10)
	    msg_format("%^s writhes in agony.", m_name);
	else
	    msg_format("%^s jerks limply.", m_name);
    }

    /* Dogs and Hounds */
    else if (strchr("CZ", r_ptr->r_char)) 
 {
	if (percentage > 95)
	    msg_format("%^s shrugs off the attack.", m_name);
	else if (percentage > 75)
	    msg_format("%^s snarls with pain.", m_name);
	else if (percentage > 50)
	    msg_format("%^s yelps in pain.", m_name);
	else if (percentage > 35)
	    msg_format("%^s howls in pain.", m_name);
	else if (percentage > 20)
	    msg_format("%^s howls in agony.", m_name);
	else if (percentage > 10)
	    msg_format("%^s writhes in agony.", m_name);
	else
	    msg_format("%^s yelps feebly.", m_name);
    }

    /* One type of monsters (ignore,squeal,shriek) */
    else if (strchr("FIKMRSXabclqrst", r_ptr->r_char)) 
 {
	if (percentage > 95)
	    msg_format("%^s ignores the attack.", m_name);
	else if (percentage > 75)
	    msg_format("%^s grunts with pain.", m_name);
	else if (percentage > 50)
	    msg_format("%^s squeals in pain.", m_name);
	else if (percentage > 35)
	    msg_format("%^s shrieks in pain.", m_name);
	else if (percentage > 20)
	    msg_format("%^s shrieks in agony.", m_name);
	else if (percentage > 10)
	    msg_format("%^s writhes in agony.", m_name);
	else
	    msg_format("%^s cries out feebly.", m_name);
    }

    /* Another type of monsters (shrug,cry,scream) */
    else 
 {
	if (percentage > 95)
	    msg_format("%^s shrugs off the attack.", m_name);
	else if (percentage > 75)
	    msg_format("%^s grunts with pain.", m_name);
	else if (percentage > 50)
	    msg_format("%^s cries out in pain.", m_name);
	else if (percentage > 35)
	    msg_format("%^s screams in pain.", m_name);
	else if (percentage > 20)
	    msg_format("%^s screams in agony.", m_name);
	else if (percentage > 10)
	    msg_format("%^s writhes in agony.", m_name);
	else
	    msg_format("%^s cries out feebly.", m_name);
    }
}

/* Determine a monster's saving throw.  For simplicity, for now, assume
   that the monster's chance is based only on its level.  Return TRUE if
   the monster succeeds, and FALSE if the monster bites it. -GJW */
bool monster_saves(int mlev, int plev)
{
    int prob = 50 + 3 * (mlev - plev);
    if (prob < 5) prob = 5;
    else if (prob > 95) prob = 95;
    return percent(prob);
}
