/*
 * File: mon-util.c
 * Purpose: Monster manipulation utilities.
 *
 * Copyright (c) 1997-2007 Ben Harrison, James E. Wilson, Robert A. Koeneke
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    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.  Other copyrights may also apply.
 */

#include "angband.h"
#include "monster/mon-make.h"
#include "monster/mon-msg.h"
#include "monster/mon-spell.h"
#include "monster/mon-timed.h"
#include "monster/mon-list.h"
#include "monster/mon-util.h"
#include "squelch.h"


/**
 * Get the lore record for this monster race.
 */
monster_lore *get_lore(const monster_race *race)
{
	assert(race);
	return &l_list[race->ridx];
}


/**
 * Returns the monster with the given name. If no monster has the exact name
 * given, returns the first monster with the given name as a (case-insensitive)
 * substring.
 */
monster_race *lookup_monster(const char *name)
{
	int i;
	monster_race *closest = NULL;
	
	/* Look for it */
	for (i = 1; i < z_info->r_max; i++)
	{
		monster_race *r_ptr = &r_info[i];
		if (!r_ptr->name)
			continue;

		/* Test for equality */
		if (streq(name, r_ptr->name))
			return r_ptr;

		/* Test for close matches */
		if (!closest && my_stristr(r_ptr->name, name))
			closest = r_ptr;
	} 

	/* Return our best match */
	return closest;
}

/**
 * Return the monster base matching the given name.
 */
monster_base *lookup_monster_base(const char *name)
{
	monster_base *base;

	/* Look for it */
	for (base = rb_info; base; base = base->next) {
		if (streq(name, base->name))
			return base;
	}

	return NULL;
}

/**
 * Return whether the given base matches any of the names given.
 *
 * Accepts a variable-length list of name strings. The list must end with NULL.
 */
bool match_monster_bases(const monster_base *base, ...)
{
	bool ok = FALSE;
	va_list vp;
	char *name;

	va_start(vp, base);
	while (!ok && ((name = va_arg(vp, char *)) != NULL))
		ok = base == lookup_monster_base(name);
	va_end(vp);

	return ok;
}

/**
 * Perform simple English pluralization on a monster name.
 */
void plural_aux(char *name, size_t max)
{
	unsigned long name_len = strlen(name);

	if (name[name_len - 1] == 's')
		my_strcat(name, "es", max);
	else
		my_strcat(name, "s", max);
}


/**
 * Helper function for display monlist.  Prints the number of creatures, followed
 * by either a singular or plural version of the race name as appropriate.
 */
void get_mon_name(char *output_name, size_t max,
		const monster_race *r_ptr, int num)
{
	assert(r_ptr);

    /* Unique names don't have a number */
	if (rf_has(r_ptr->flags, RF_UNIQUE)) {
		my_strcpy(output_name, "[U] ", max);
        my_strcat(output_name, r_ptr->name, max);
        return;
    }

    my_strcpy(output_name, format("%3d ", num), max);

    if (num == 1) {
        my_strcat(output_name, r_ptr->name, max);
        return;
    }

    if (r_ptr->plural != NULL) {
        my_strcat(output_name, r_ptr->plural, max);
    }
    else {
        char race_name[80];
		my_strcpy(race_name, r_ptr->name, sizeof(race_name));
        plural_aux(race_name, sizeof(race_name));
        my_strcat(output_name, race_name, max);
    }
}

/**
 * Builds 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.
 *
 * I am assuming that no monster name is more than 65 characters long,
 * so that "char desc[80];" is sufficiently large for any result, even
 * when the "offscreen" notation is added.
 *
 * Note that the "possessive" for certain unique monsters will look
 * really silly, as in "Morgoth, King of Darkness's".  We should
 * perhaps add a flag to "remove" any "descriptives" in the name.
 *
 * Note that "offscreen" monsters will get a special "(offscreen)"
 * notation in their name if they are visible but offscreen.  This
 * may look silly with possessives, as in "the rat's (offscreen)".
 * Perhaps the "offscreen" descriptor should be abbreviated.
 *
 * 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
 *  0x100 --> Capitalise monster name
 *
 * Useful Modes:
 *   0x00 --> Full nominative name ("the kobold") or "it"
 *   0x04 --> Full nominative name ("the kobold") or "something"
 *   0x80 --> Banishment 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, size_t max, const struct monster *mon, int mode)
{
	const char *choice;
	bool seen, use_pronoun;

	assert(mon);


	/* Can we "see" it (forced, or not hidden + visible) */
	seen = ((mode & (MDESC_SHOW)) || (!(mode & (MDESC_HIDE)) && mon->ml));

	/* Sexed Pronouns (seen and forced, or unseen and allowed) */
	use_pronoun = ((seen && (mode & (MDESC_PRO_VIS))) || (!seen && (mode & (MDESC_PRO_HID))));


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

		/* Extract the gender (if applicable) */
		if (rf_has(mon->race->flags, RF_FEMALE)) msex = 0x20;
		else if (rf_has(mon->race->flags, RF_MALE)) msex = 0x10;

		/* Ignore the gender (if desired) */
		if (!mon || !use_pronoun) msex = 0x00;


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

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

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

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

		/* Copy the result */
		my_strcpy(desc, choice, max);
	}


	/* Handle visible monsters, "reflexive" request */
	else if ((mode & MDESC_POSS) && (mode & MDESC_OBJE))
	{
		/* The monster is visible, so use its gender */
		if (rf_has(mon->race->flags, RF_FEMALE)) my_strcpy(desc, "herself", max);
		else if (rf_has(mon->race->flags, RF_MALE)) my_strcpy(desc, "himself", max);
		else my_strcpy(desc, "itself", max);
	}


	/* Handle all other visible monster requests */
	else
	{
		/* It could be a Unique */
		if (rf_has(mon->race->flags, RF_UNIQUE))
		{
			/* Start with the name (thus nominative and objective) */
			my_strcpy(desc, mon->race->name, max);
		}

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

			/* Indefinite monsters need an indefinite article */
			my_strcpy(desc, is_a_vowel(mon->race->name[0]) ? "an " : "a ", max);
			my_strcat(desc, mon->race->name, max);
		}

		/* It could be a normal, definite, monster */
		else
		{
			/* Definite monsters need a definite article */
			my_strcpy(desc, "the ", max);
			my_strcat(desc, mon->race->name, max);
		}

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

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

		/* Mention "offscreen" monsters XXX XXX */
		if (!panel_contains(mon->fy, mon->fx))
		{
			/* Append special notation */
			my_strcat(desc, " (offscreen)", max);
		}
	}

	if (mode & MDESC_CAPITAL)
		my_strcap(desc);
}



/**
 * This function updates the monster record of the given monster
 *
 * This involves extracting the distance to the player (if requested),
 * and then checking for visibility (natural, infravision, see-invis,
 * telepathy), updating the monster visibility flag, redrawing (or
 * erasing) the monster when its visibility changes, and taking note
 * of any interesting monster flags (cold-blooded, invisible, etc).
 *
 * Note the new "mflag" field which encodes several monster state flags,
 * including "view" for when the monster is currently in line of sight,
 * and "mark" for when the monster is currently visible via detection.
 *
 * The only monster fields that are changed here are "cdis" (the
 * distance from the player), "ml" (visible to the player), and
 * "mflag" (to maintain the "MFLAG_VIEW" flag).
 *
 * Note the special "update_monsters()" function which can be used to
 * call this function once for every monster.
 *
 * Note the "full" flag which requests that the "cdis" field be updated;
 * this is only needed when the monster (or the player) has moved.
 *
 * Every time a monster moves, we must call this function for that
 * monster, and update the distance, and the visibility.  Every time
 * the player moves, we must call this function for every monster, and
 * update the distance, and the visibility.  Whenever the player "state"
 * changes in certain ways ("blindness", "infravision", "telepathy",
 * and "see invisible"), we must call this function for every monster,
 * and update the visibility.
 *
 * Routines that change the "illumination" of a grid must also call this
 * function for any monster in that grid, 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.  When the player is running, this function is one
 * of the primary bottlenecks, along with "update_view()" and the
 * "process_monsters()" code, so efficiency is important.
 *
 * Note the optimized "inline" version of the "distance()" function.
 *
 * A monster is "visible" to the player if (1) it has been detected
 * by the player, (2) it is close to the player and the player has
 * telepathy, or (3) it is close to the player, and in line of sight
 * of the player, and it is "illuminated" by some combination of
 * infravision, torch light, or permanent light (invisible monsters
 * are only affected by "light" if the player can see invisible).
 *
 * Monsters which are not on the current panel may be "visible" to
 * the player, and their descriptions will include an "offscreen"
 * reference.  Currently, offscreen monsters cannot be targeted
 * or viewed directly, but old targets will remain set.  XXX XXX
 *
 * The player can choose to be disturbed by several things, including
 * "OPT(disturb_near)" (monster which is "easily" viewable moves in some
 * way).  Note that "moves" includes "appears" and "disappears".
 */
void update_mon(struct monster *m_ptr, bool full)
{
	monster_lore *l_ptr;

	int d;

	/* Current location */
	int fy, fx;

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

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

	assert(m_ptr != NULL);

	l_ptr = get_lore(m_ptr->race);
	
	fy = m_ptr->fy;
	fx = m_ptr->fx;

	/* Compute distance */
	if (full) {
		int py = p_ptr->py;
		int px = p_ptr->px;

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

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

		/* Restrict distance */
		if (d > 255) d = 255;

		/* Save the distance */
		m_ptr->cdis = d;
	}

	/* Extract distance */
	else {
		/* Extract the distance */
		d = m_ptr->cdis;
	}

	/* Detected */
	if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;

	/* Nearby */
	if (d <= MAX_SIGHT) {
		/* Basic telepathy */
		if (check_state(p_ptr, OF_TELEPATHY, p_ptr->state.flags)) {
			/* Empty mind, no telepathy */
			if (rf_has(m_ptr->race->flags, RF_EMPTY_MIND))
			{
				/* Nothing! */
			}

			/* Weird mind, occasional telepathy */
			else if (rf_has(m_ptr->race->flags, RF_WEIRD_MIND)) {
				/* One in ten individuals are detectable */
				if ((m_ptr->midx % 10) == 5) {
					/* Detectable */
					flag = TRUE;

					/* Check for LOS so that MFLAG_VIEW is set later */
					if (player_has_los_bold(fy, fx)) easy = TRUE;
				}
			}

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

				/* Check for LOS to that MFLAG_VIEW is set later */
				if (player_has_los_bold(fy, fx)) easy = TRUE;
			}
		}

		/* Normal line of sight and player is not blind */
		if (player_has_los_bold(fy, fx) && !p_ptr->timed[TMD_BLIND]) {
			/* Use "infravision" */
			if (d <= p_ptr->state.see_infra) {
				/* Learn about warm/cold blood */
				rf_on(l_ptr->flags, RF_COLD_BLOOD);

				/* Handle "warm blooded" monsters */
				if (!rf_has(m_ptr->race->flags, RF_COLD_BLOOD)) {
					/* Easy to see */
					easy = flag = TRUE;
				}
			}

			/* See if the monster is emitting light */
			/*if (rf_has(m_ptr->race->flags, RF_HAS_LIGHT)) easy = flag = TRUE;*/

			/* Use "illumination" */
			if (player_can_see_bold(fy, fx)) {
				/* Learn it emits light */
				rf_on(l_ptr->flags, RF_HAS_LIGHT);

				/* Learn about invisibility */
				rf_on(l_ptr->flags, RF_INVISIBLE);

				/* Handle "invisible" monsters */
				if (rf_has(m_ptr->race->flags, RF_INVISIBLE)) {
					/* See invisible */
					if (check_state(p_ptr, OF_SEE_INVIS, p_ptr->state.flags))
					{
						/* Easy to see */
						easy = flag = TRUE;
					}
				}

				/* Handle "normal" monsters */
				else {
					/* Easy to see */
					easy = flag = TRUE;
				}
			}
		}
	}

	/* If a mimic looks like a squelched item, it's not seen */
	if (is_mimicking(m_ptr)) {
		object_type *o_ptr = object_byid(m_ptr->mimicked_o_idx);
		if (squelch_item_ok(o_ptr))
			easy = flag = FALSE;
	}
	
	/* The monster is now visible */
	if (flag) {
		/* Learn about the monster's mind */
		if (check_state(p_ptr, OF_TELEPATHY, p_ptr->state.flags))
			flags_set(l_ptr->flags, RF_SIZE, RF_EMPTY_MIND, RF_WEIRD_MIND,
					RF_SMART, RF_STUPID, FLAG_END);

		/* It was previously unseen */
		if (!m_ptr->ml) {
			/* Mark as visible */
			m_ptr->ml = TRUE;

			/* Draw the monster */
			cave_light_spot(cave, fy, fx);

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

			/* Hack -- Count "fresh" sightings */
			if (l_ptr->sights < MAX_SHORT)
				l_ptr->sights++;

			/* Window stuff */
			p_ptr->redraw |= PR_MONLIST;
		}
	}

	/* The monster is not visible */
	else {
		/* It was previously seen */
		if (m_ptr->ml) {
			/* Treat mimics differently */
			if (!m_ptr->mimicked_o_idx || 
					squelch_item_ok(object_byid(m_ptr->mimicked_o_idx)))
			{
				/* Mark as not visible */
				m_ptr->ml = FALSE;

				/* Erase the monster */
				cave_light_spot(cave, fy, fx);

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

				/* Window stuff */
				p_ptr->redraw |= PR_MONLIST;
			}
		}
	}


	/* The monster is now easily visible */
	if (easy) {
		/* Change */
		if (!(m_ptr->mflag & (MFLAG_VIEW))) {
			/* Mark as easily visible */
			m_ptr->mflag |= (MFLAG_VIEW);

			/* Disturb on appearance */
			if (OPT(disturb_near)) disturb(p_ptr, 1, 0);

			/* Re-draw monster window */
			p_ptr->redraw |= PR_MONLIST;
		}
	}

	/* The monster is not easily visible */
	else {
		/* Change */
		if (m_ptr->mflag & (MFLAG_VIEW)) {
			/* Mark as not easily visible */
			m_ptr->mflag &= ~(MFLAG_VIEW);

			/* Disturb on disappearance */
			if (OPT(disturb_near) && !is_mimicking(m_ptr)) disturb(p_ptr, 1, 0);

			/* Re-draw monster list window */
			p_ptr->redraw |= PR_MONLIST;
		}
	}
}




/**
 * Updates all the (non-dead) monsters via update_mon().
 */
void update_monsters(bool full)
{
	int i;

	/* Update each (live) monster */
	for (i = 1; i < cave_monster_max(cave); i++) {
		monster_type *m_ptr = cave_monster(cave, i);

		/* Update the monster if alive */
		if (m_ptr->race)
			update_mon(m_ptr, full);
	}
}


/**
 * Add the given object to the given monster's inventory.
 *
 * Returns the o_idx of the new object, or 0 if the object is
 * not successfully added.
 */
s16b monster_carry(struct monster *m_ptr, object_type *j_ptr)
{
	s16b o_idx;

	s16b this_o_idx, next_o_idx = 0;

	/* Scan objects already being held for combination */
	for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) {
		object_type *o_ptr;

		/* Get the object */
		o_ptr = object_byid(this_o_idx);

		/* Get the next object */
		next_o_idx = o_ptr->next_o_idx;

		/* Check for combination */
		if (object_similar(o_ptr, j_ptr, OSTACK_MONSTER)) {
			/* Combine the items */
			object_absorb(o_ptr, j_ptr);

			/* Result */
			return (this_o_idx);
		}
	}


	/* Make an object */
	o_idx = o_pop();

	/* Success */
	if (o_idx) {
		object_type *o_ptr;

		/* Get new object */
		o_ptr = object_byid(o_idx);

		/* Copy object */
		object_copy(o_ptr, j_ptr);

		/* Forget mark */
		o_ptr->marked = FALSE;

		/* Forget location */
		o_ptr->iy = o_ptr->ix = 0;

		/* Link the object to the monster */
		o_ptr->held_m_idx = m_ptr->midx;

		/* Link the object to the pile */
		o_ptr->next_o_idx = m_ptr->hold_o_idx;

		/* Link the monster to the object */
		m_ptr->hold_o_idx = o_idx;
	}

	/* Result */
	return (o_idx);
}

/**
 * Swap the players/monsters (if any) at two locations.
 */
void monster_swap(int y1, int x1, int y2, int x2)
{
	int m1, m2;

	monster_type *m_ptr;

	/* Monsters */
	m1 = cave->m_idx[y1][x1];
	m2 = cave->m_idx[y2][x2];

	/* Update grids */
	cave->m_idx[y1][x1] = m2;
	cave->m_idx[y2][x2] = m1;

	/* Monster 1 */
	if (m1 > 0) {
		m_ptr = cave_monster(cave, m1);

		/* Move monster */
		m_ptr->fy = y2;
		m_ptr->fx = x2;

		/* Update monster */
		update_mon(m_ptr, TRUE);

		/* Radiate light? */
		if (rf_has(m_ptr->race->flags, RF_HAS_LIGHT))
			p_ptr->update |= PU_UPDATE_VIEW;

		/* Redraw monster list */
		p_ptr->redraw |= (PR_MONLIST);
	}

	/* Player 1 */
	else if (m1 < 0) {
		/* Move player */
		p_ptr->py = y2;
		p_ptr->px = x2;

		/* Update the trap detection status */
		p_ptr->redraw |= (PR_DTRAP);

		/* Update the panel */
		p_ptr->update |= (PU_PANEL);

		/* Update the visuals (and monster distances) */
		p_ptr->update |= (PU_UPDATE_VIEW | PU_DISTANCE);

		/* Update the flow */
		p_ptr->update |= (PU_UPDATE_FLOW);

		/* Redraw monster list */
		p_ptr->redraw |= (PR_MONLIST);
	}

	/* Monster 2 */
	if (m2 > 0) {
		m_ptr = cave_monster(cave, m2);

		/* Move monster */
		m_ptr->fy = y1;
		m_ptr->fx = x1;

		/* Update monster */
		update_mon(m_ptr, TRUE);

		/* Radiate light? */
		if (rf_has(m_ptr->race->flags, RF_HAS_LIGHT))
			p_ptr->update |= PU_UPDATE_VIEW;

		/* Redraw monster list */
		p_ptr->redraw |= (PR_MONLIST);
	}

	/* Player 2 */
	else if (m2 < 0) {
		/* Move player */
		p_ptr->py = y1;
		p_ptr->px = x1;

		/* Update the trap detection status */
		p_ptr->redraw |= (PR_DTRAP);

		/* Update the panel */
		p_ptr->update |= (PU_PANEL);

		/* Update the visuals (and monster distances) */
		p_ptr->update |= (PU_UPDATE_VIEW | PU_DISTANCE);

		/* Update the flow */
		p_ptr->update |= (PU_UPDATE_FLOW);

		/* Redraw monster list */
		p_ptr->redraw |= (PR_MONLIST);
	}

	/* Redraw */
	cave_light_spot(cave, y1, x1);
	cave_light_spot(cave, y2, x2);
}

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

/*
 * Hack - the kin type for S_KIN
 */
wchar_t summon_kin_type;


/**
 * Hack -- help decide if a monster race is "okay" to summon.
 *
 * Compares the given monster to the monster type specified by
 * summon_specific_type. Returns TRUE if the monster is eligible to
 * be summoned, FALSE otherwise. 
 */
static bool summon_specific_okay(monster_race *race)
{
	bool unique = rf_has(race->flags, RF_UNIQUE);
	bool scary = flags_test(race->flags, RF_SIZE, RF_UNIQUE, FLAG_END);

	/* Check our requirements */
	switch (summon_specific_type) {
		case S_ANIMAL: return !unique && rf_has(race->flags, RF_ANIMAL);
		case S_SPIDER: return !unique && match_monster_bases(race->base, "spider", NULL);
		case S_HOUND: return !unique && match_monster_bases(race->base, "canine", "zephyr hound", NULL);
		case S_HYDRA: return !unique && match_monster_bases(race->base, "hydra", NULL);
		case S_AINU: return !scary && match_monster_bases(race->base, "ainu", NULL);
		case S_DEMON: return !scary && rf_has(race->flags, RF_DEMON);
		case S_UNDEAD: return !scary && rf_has(race->flags, RF_UNDEAD);
		case S_DRAGON: return !scary && rf_has(race->flags, RF_DRAGON);
		case S_KIN: return !unique && race->d_char == summon_kin_type;
		case S_HI_UNDEAD: return match_monster_bases(race->base, "lich", "vampire", "wraith", NULL);
		case S_HI_DRAGON: return match_monster_bases(race->base, "ancient dragon", NULL);
		case S_HI_DEMON: return match_monster_bases(race->base, "major demon", NULL);
		case S_WRAITH: return unique && match_monster_bases(race->base, "wraith", NULL);
		case S_UNIQUE: return unique;
		case S_MONSTER: return !scary;
		case S_MONSTERS: return !unique;

		default: return TRUE;
	}
}


/**
 * Places 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: S_UNIQUE and S_WRAITH (XXX) will summon Uniques
 * Note: S_HI_UNDEAD and S_HI_DRAGON may summon Uniques
 * Note: None of the other summon codes will ever summon Uniques.
 *
 * 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 we use the new "monster allocation table" creation code
 * to restrict the "get_mon_num()" function to the set of "legal"
 * monsters, making this function much faster and more reliable.
 *
 * Note that this function may not succeed, though this is very rare.
 */
int summon_specific(int y1, int x1, int lev, int type, int delay)
{
	int i, x = 0, y = 0;

	monster_type *m_ptr;
	monster_race *race;

	/* Look for a location, allow up to 4 squares away */
	for (i = 0; i < 60; ++i)
	{
		/* Pick a distance */
		int d = (i / 15) + 1;

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

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

		/* Hack -- no summon on glyph of warding */
		if (cave_iswarded(cave, y, x)) continue;

		/* Okay */
		break;
	}

	/* Failure */
	if (i == 60) return (0);

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

	/* Prepare allocation table */
	get_mon_num_prep(summon_specific_okay);

	/* Pick a monster, using the level calculation */
	race = get_mon_num((p_ptr->depth + lev) / 2 + 5);

	/* Prepare allocation table */
	get_mon_num_prep(NULL);

	/* Handle failure */
	if (!race) return (0);

	/* Attempt to place the monster (awake, don't allow groups) */
	if (!place_new_monster(cave, y, x, race, FALSE, FALSE, ORIGIN_DROP_SUMMON))
		return (0);

	/* Success, return the level of the monster */
	m_ptr = cave_monster_at(cave, y, x);
	
	/* If delay, try to let the player act before the summoned monsters,
	 * including slowing down faster monsters for one turn */
	if (delay) {
		m_ptr->energy = 0;
		if (m_ptr->race->speed > p_ptr->state.speed)
			mon_inc_timed(m_ptr, MON_TMD_SLOW, 1,
				MON_TMD_FLG_NOMESSAGE, FALSE);
	}

	return (m_ptr->race->level);
}

/**
 * Lets the given monster attempt to reproduce.
 *
 * Note that "reproduction" REQUIRES empty space.
 *
 * Returns TRUE if the monster successfully reproduced.
 */
bool multiply_monster(const monster_type *m_ptr)
{
	int i, y, x;

	bool result = FALSE;

	/* 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, TRUE);

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

		/* Create a new monster (awake, no groups) */
		result = place_new_monster(cave, y, x, m_ptr->race, FALSE, FALSE,
			ORIGIN_DROP_BREED);

		/* Done */
		break;
	}

	/* Result */
	return (result);
}


/**
 * Make player fully aware of the given mimic.
 *
 * When a player becomes aware of a mimic, we update the monster memory
 * and delete the "fake item" that the monster was mimicking.
 */
void become_aware(struct monster *m_ptr)
{
	monster_lore *l_ptr = get_lore(m_ptr->race);

	if (m_ptr->unaware) {
		m_ptr->unaware = FALSE;

		/* Learn about mimicry */
		if (rf_has(m_ptr->race->flags, RF_UNAWARE))
			rf_on(l_ptr->flags, RF_UNAWARE);

		/* Delete any false items */
		if (m_ptr->mimicked_o_idx > 0) {
			object_type *o_ptr = object_byid(m_ptr->mimicked_o_idx);
			char o_name[80];
			object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE);

			/* Print a message */
			msg("The %s was really a monster!", o_name);

			/* Clear the mimicry */
			o_ptr->mimicking_m_idx = 0;

			/* Give the object to the monster if appropriate */
			if (rf_has(m_ptr->race->flags, RF_MIMIC_INV)) {
				object_type *i_ptr;
				object_type object_type_body;
				
				/* Get local object */
				i_ptr = &object_type_body;

				/* Obtain local object */
				object_copy(i_ptr, o_ptr);

				/* Carry the object */
				monster_carry(m_ptr, i_ptr);
			}
				
			/* Delete the mimicked object */
			delete_object_idx(m_ptr->mimicked_o_idx);
			m_ptr->mimicked_o_idx = 0;
		}
		
		/* Update monster and item lists */
		p_ptr->update |= (PU_UPDATE_VIEW | PU_MONSTERS);
		p_ptr->redraw |= (PR_MONLIST | PR_ITEMLIST);
	}
}

/**
 * Returns TRUE if the given monster is currently mimicking an item.
 */
bool is_mimicking(struct monster *m_ptr)
{
	return (m_ptr->unaware && m_ptr->mimicked_o_idx);
}


/**
 * The given monster learns about an "observed" resistance or other player
 * state property, or lack of it.
 */
void update_smart_learn(struct monster *m, struct player *p, int flag)
{
	/* Sanity check */
	if (!flag) return;

	/* anything a monster might learn, the player should learn */
	wieldeds_notice_flag(p, flag);

	/* Not allowed to learn */
	if (!OPT(birth_ai_learn)) return;

	/* Too stupid to learn anything */
	if (rf_has(m->race->flags, RF_STUPID)) return;

	/* Not intelligent, only learn sometimes */
	if (!rf_has(m->race->flags, RF_SMART) && one_in_(2)) return;

	/* Analyze the knowledge; fail very rarely */
	if (check_state(p, flag, p->state.flags) && !one_in_(100))
		of_on(m->known_pflags, flag);
	else
		of_off(m->known_pflags, flag);
}

