/* File: borg5.c */

/* Purpose: Medium level stuff for the Borg -BEN- */

#include "angband.h"

#ifdef ALLOW_BORG

#include "borg1.h"
#include "borg2.h"
#include "borg3.h"
#include "borg4.h"
#include "borg5.h"



/*
 * This file is responsible for the "borg_update" routine, which is used
 * to notice changes in the world, and to keep track of terrain features,
 * objects, monsters, both from visual changes, and from world messages.
 *
 * One big thing this file does is "object/monster tracking", which
 * attempts to gather information about the objects and monsters in
 * the dungeon, including their identity, location, and state, and
 * to "follow" them if they "move", and to delete them when they die.
 *
 * Information about terrain is used to help plan "flow" paths.  Info
 * about objects and monsters is used to optimize planning paths to
 * those objects and monsters.  Info about monsters is also used for
 * the "danger" functions, which help avoid dangerous situations.
 *
 * Notes:
 *   We assume that monsters/objects can never appear in walls/doors
 *   We count the occurance of invisible or offscreen monsters
 *   We treat "mimics" and "trappers" as "invisible" monsters
 *
 * To Do:
 *   Analyze the probable danger of offscreen monsters
 *   Track approximate monster hitpoints (min/max hitpoints?)
 *   If so, factor in regeneration and various spell attacks
 *   Take account of monster "fear" when "following" monsters
 *
 * Bugs:
 *   Groups of monsters may induce faulty monster matching
 *   Teleporting monsters may induce faulty monster matching
 *   Monsters which appear singly and in groups are "weird"
 *   The timestamps are not quite in sync properly (?)
 */


/*
 * Old values
 */

static s16b old_depth = -1;	/* Previous depth */

static s16b old_chp = -1;	/* Previous hit points */
static s16b old_csp = -1;	/* Previous spell points */

static int o_w_x = -1;		/* Old panel */
static int o_w_y = -1;		/* Old panel */

static int o_c_x = -1;		/* Old location */
static int o_c_y = -1;		/* Old location */


/*
 * Hack -- message memory
 */

static s16b auto_msg_len;

static s16b auto_msg_siz;

static char *auto_msg_buf;

static s16b auto_msg_num;

static s16b auto_msg_max;

static s16b *auto_msg_pos;

static s16b *auto_msg_use;


/*
 * Hack -- help identify "unique" monster names
 */

static int auto_unique_size;		/* Number of uniques */
static s16b *auto_unique_what;		/* Indexes of uniques */
static cptr *auto_unique_text;		/* Names of uniques */

/*
 * Hack -- help identify "normal" monster names
 */

static int auto_normal_size;		/* Number of normals */
static s16b *auto_normal_what;		/* Indexes of normals */
static cptr *auto_normal_text;		/* Names of normals */



/*
 * Hack -- monster/object tracking grids
 */

typedef struct auto_wank auto_wank;

struct auto_wank
{
	byte x;
	byte y;

	byte t_a;
	char t_c;

	bool is_take;
	bool is_kill;
};



/*
 * Hack -- object/monster tracking array
 */

static int auto_wank_num = 0;

static auto_wank *auto_wanks;




/*
 * Attempt to guess what kind of object is at the given location.
 *
 * This routine should rarely, if ever, return "zero".
 *
 * Hack -- we use "base level" instead of "allocation levels".
 */
static int borg_guess_kind(byte a, char c)
{
	int i, s;

	int b_i = 0, b_s = 0;


	/* Find an "acceptable" object */
	for (i = 1; i < MAX_K_IDX; i++)
	{
		object_kind *k_ptr = &k_info[i];

		/* Skip non-objects */
		if (!k_ptr->name) continue;


		/* Base score */
		s = 10000;


		/* Hack -- penalize "extremely" out of depth */
		if (k_ptr->level > auto_depth + 50) s = s - 500;

		/* Hack -- penalize "very" out of depth */
		if (k_ptr->level > auto_depth + 15) s = s - 100;

		/* Hack -- penalize "rather" out of depth */
		if (k_ptr->level > auto_depth + 5) s = s - 50;

		/* Hack -- penalize "somewhat" out of depth */
		if (k_ptr->level > auto_depth) s = s - 10;

		/* Hack -- Penalize "depth miss" */
		s = s - ABS(k_ptr->level - auto_depth);


		/* Hack -- Penalize INSTA_ART items */
		if (k_ptr->flags3 & TR3_INSTA_ART) s = s - 1000;


		/* Hack -- Penalize CURSED items */
		if (k_ptr->flags3 & TR3_LIGHT_CURSE) s = s - 5000;

		/* Hack -- Penalize BROKEN items */
		if (k_ptr->cost <= 0) s = s - 5000;


		/* Verify char */
		if (c != k_ptr->x_char) continue;

		/* Flavored objects */
		if (k_ptr->has_flavor)
		{
			/* Hack -- penalize "flavored" objects */
			s = s - 20;
		}

		/* Normal objects */
		else
		{
			/* Verify attr */
			if (a != k_ptr->x_attr) continue;
		}


		/* Desire "best" possible score */
		if (b_i && (s < b_s)) continue;

		/* Track it */
		b_i = i; b_s = s;
	}

	/* Result */
	return (b_i);
}


/*
 * Delete an old "object" record
 */
static void borg_delete_take(int i)
{
	auto_grid *ag;

	auto_take *take = &auto_takes[i];

	/* Paranoia -- Already wiped */
	if (!take->k_idx) return;

	/* Note */
	borg_note(format("# Forgetting an object '%s' at (%d,%d)",
	                 (k_name + k_info[take->k_idx].name),
	                 take->x, take->y));

	/* Access the grid */
	ag = &auto_grids[take->y][take->x];

	/* Forget it */
	ag->take = 0;

	/* Kill the object */
	WIPE(take, auto_take);

	/* One less object */
	auto_takes_cnt--;

	/* Wipe goals */
	goal = 0;
}


/*
 * Determine if an object should be "viewable"
 */
static bool borg_follow_take_aux(int i, int y, int x)
{
	auto_grid *ag;

	auto_take *take = &auto_takes[i];


	/* Access the grid */
	ag = &auto_grids[y][x];

	/* Not on-screen */
	if (!(ag->info & BORG_OKAY)) return (FALSE);


	/* Assume viewable */
	return (TRUE);
}


/*
 * Attempt to "follow" a missing object
 *
 * This routine is not called when the player is blind or hallucinating.
 *
 * This function just deletes objects which have disappeared.
 *
 * We assume that a monster walking onto an object destroys the object.
 */
static void borg_follow_take(int i)
{
	int ox, oy;

	auto_take *take = &auto_takes[i];


	/* Paranoia */
	if (!take->k_idx) return;


	/* Old location */
	ox = take->x;
	oy = take->y;


	/* Out of sight */
	if (!borg_follow_take_aux(i, oy, ox)) return;


	/* Note */
	borg_note(format("# There was an object '%s' at (%d,%d)",
	                 (k_name + k_info[take->k_idx].name),
	                 ox, oy));


	/* Kill the object */
	borg_delete_take(i);
}



/*
 * Obtain a new "take" index
 */
static int borg_new_take(int k_idx, int y, int x)
{
	int i, n = -1;

	auto_take *take;

	auto_grid *ag = &auto_grids[y][x];


	/* Look for a "dead" object */
	for (i = 1; (n < 0) && (i < auto_takes_nxt); i++)
	{
		/* Reuse "dead" objects */
		if (!auto_takes[i].k_idx) n = i;
	}

	/* Allocate a new object */
	if ((n < 0) && (auto_takes_nxt < 256))
	{
		/* Acquire the entry, advance */
		n = auto_takes_nxt++;
	}

	/* Hack -- steal an old object */
	if (n < 0)
	{
		/* Note */
		borg_note("# Too many objects");

		/* Hack -- Pick a random object */
		n = rand_int(auto_takes_nxt-1) + 1;

		/* Delete it */
		borg_delete_take(n);
	}


	/* Count new object */
	auto_takes_cnt++;

	/* Obtain the object */
	take = &auto_takes[n];

	/* Save the kind */
	take->k_idx = k_idx;

	/* Save the location */
	take->x = x;
	take->y = y;

	/* Save the index */
	ag->take = n;

	/* Timestamp */
	take->when = c_t;

	/* Note */
	borg_note(format("# Creating an object '%s' at (%d,%d)",
	                 (k_name + k_info[take->k_idx].name),
	                 take->x, take->y));

	/* Wipe goals */
	goal = 0;

	/* Result */
	return (n);
}



/*
 * Attempt to notice a changing "take"
 */
static bool observe_take_diff(int y, int x, byte a, char c)
{
	int i, k_idx;

	auto_take *take;

	/* Guess the kind */
	k_idx = borg_guess_kind(a, c);

	/* Oops */
	if (!k_idx) return (FALSE);

	/* Make a new object */
	i = borg_new_take(k_idx, y, x);

	/* Get the object */
	take = &auto_takes[i];

	/* Timestamp */
	take->when = c_t;

	/* Okay */
	return (TRUE);
}


/*
 * Attempt to "track" a "take" at the given location
 * Assume that the object has not moved more than "d" grids
 * Note that, of course, objects are never supposed to move,
 * but we may want to take account of "falling" missiles later.
 */
static bool observe_take_move(int y, int x, int d, byte a, char c)
{
	int i, z, ox, oy;

	object_kind *k_ptr;

	/* Scan the objects */
	for (i = 1; i < auto_takes_nxt; i++)
	{
		auto_take *take = &auto_takes[i];

		/* Skip dead objects */
		if (!take->k_idx) continue;

		/* Skip assigned objects */
		if (take->seen) continue;

		/* Extract old location */
		ox = take->x;
		oy = take->y;

		/* Calculate distance */
		z = distance(oy, ox, y, x);

		/* Possible match */
		if (z > d) continue;

		/* Access the kind */
		k_ptr = &k_info[take->k_idx];

		/* Require matching char */
		if (c != k_ptr->x_char) continue;

		/* Require matching attr */
		if (a != k_ptr->x_attr)
		{
			/* Ignore "flavored" colors */
			if (!k_ptr->has_flavor) continue;
		}

		/* Actual movement (?) */
		if (z)
		{
			/* Update the grids */
			auto_grids[take->y][take->x].take = 0;

			/* Track it */
			take->x = x;
			take->y = y;

			/* Update the grids */
			auto_grids[take->y][take->x].take = i;

			/* Note */
			borg_note(format("# Tracking an object '%s' at (%d,%d) from (%d,%d)",
			                 (k_name + k_ptr->name),
			                 take->x, take->y, ox, oy));

			/* Clear goals */
			goal = 0;
		}

		/* Timestamp */
		take->when = c_t;

		/* Mark as seen */
		take->seen = TRUE;

		/* Done */
		return (TRUE);
	}

	/* Oops */
	return (FALSE);
}




/*
 * Attempt to guess what type of monster is at the given location.
 *
 * If we are unable to think of any monster that could be at the
 * given location, we will assume the monster is a player ghost.
 * This is a total hack, but may prevent crashes.
 *
 * The guess can be improved by the judicious use of a specialized
 * "attr/char" mapping, especially for unique monsters.  Currently,
 * the Borg does not stoop to such redefinitions.
 *
 * We will probably fail to identify "trapper" and "lurker" monsters,
 * since they look like whatever they are standing on.  Now we will
 * probably just assume they are player ghosts.  XXX XXX XXX
 *
 * Note that "town" monsters may only appear in town, and in "town",
 * only "town" monsters may appear, unless we summon or polymorph
 * a monster while in town, which should never happen.
 *
 * To guess which monster is at the given location, we consider every
 * possible race, keeping the race (if any) with the best "score".
 *
 * Certain monster races are "impossible", including town monsters
 * in the dungeon, dungeon monsters in the town, unique monsters
 * known to be dead, monsters more than 50 levels out of depth,
 * and monsters with an impossible char, or an impossible attr.
 *
 * Certain aspects of a monster race are penalized, including extreme
 * out of depth, minor out of depth, clear/multihued attrs.
 *
 * Certain aspects of a monster race are rewarded, including monsters
 * that appear in groups, monsters that reproduce, monsters that have
 * been seen on this level a lot.
 *
 * We are never called for "trapper", "lurker", or "mimic" monsters.
 *
 * The actual rewards and penalties probably need some tweaking.
 *
 * Hack -- try not to choose "unique" monsters, or we will flee a lot.
 */
static int borg_guess_race(byte a, char c, bool multi)
{
	int i, s, n;

	int b_i = 0, b_s = 0;


	/* Find an "acceptable" monster */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;


		/* Base score */
		s = 10000;


		/* Verify char */
		if (c != r_ptr->x_char) continue;

		/* Clear or multi-hued monsters */
		if (r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR))
		{
			/* Penalize "weird" monsters */
			if (!multi) s = s - 1000;
		}

		/* Normal monsters */
		else
		{
			/* Verify multi */
			if (multi) continue;

			/* Verify attr */
			if (a != r_ptr->x_attr) continue;
		}


		/* Check uniques */
		if (r_ptr->flags1 & RF1_UNIQUE)
		{
			/* Hack -- Dead uniques stay dead */
			if (auto_race_death[i] > 0) continue;

			/* Prefer normals */
			s = s - 10;
		}


		/* Hack -- penalize "extremely" out of depth */
		if (r_ptr->level > auto_depth + 50) continue;

		/* Hack -- penalize "very" out of depth */
		if (r_ptr->level > auto_depth + 15) s = s - 100;

		/* Hack -- penalize "rather" out of depth */
		if (r_ptr->level > auto_depth + 5) s = s - 50;

		/* Hack -- penalize "somewhat" out of depth */
		if (r_ptr->level > auto_depth) s = s - 10;

		/* Penalize "depth miss" */
		s = s - ABS(r_ptr->level - auto_depth);


		/* Hack -- Reward group monsters */
		if (r_ptr->flags1 & (RF1_FRIEND | RF1_FRIENDS)) s = s + 5;

		/* Hack -- Reward multiplying monsters */
		if (r_ptr->flags2 & RF2_MULTIPLY) s = s + 10;


		/* Count occurances */
		n = auto_race_count[i];

		/* Mega-Hack -- Reward occurances XXX XXX XXX */
		s = s + (n / 100) + (((n < 100) ? n : 100) / 10) + ((n < 10) ? n : 10);


		/* Desire "best" possible score */
		if (b_i && (s < b_s)) continue;

		/* Track it */
		b_i = i; b_s = s;
	}

	/* Success */
	if (b_i) return (b_i);


	/* Message */
	borg_note(format("# Assuming player ghost (char %d, attr %d)", c, a));

	/* Assume player ghost */
	return (MAX_R_IDX - 1);
}


/*
 * Attempt to convert a monster name into a race index
 *
 * First we check for all possible "unique" monsters, including
 * ones we have killed, and even if the monster name is "prefixed"
 * (as in "The Tarrasque" and "The Lernean Hydra").  Since we use
 * a fast binary search, this is acceptable.
 *
 * Otherwise, if the monster is NOT named "The xxx", we assume it
 * must be a "player ghost" (which is impossible).
 *
 * Then, we do a binary search on all "normal" monster names, using
 * a search which is known to find the last matching entry, if one
 * exists, and otherwise to find an entry which would follow the
 * matching entry if there was one, unless the matching entry would
 * follow all the existing entries, in which case it will find the
 * final entry in the list.  Thus, we can search *backwards* from
 * the result of the search, and know that we will access all of
 * the matching entries, as long as we stop once we find an entry
 * which does not match, since this will catch all cases above.
 *
 * Finally, we assume the monster must be a "player ghost" (which
 * as noted above is impossible), which is a hack, but may prevent
 * crashes, even if it does induce strange behavior.
 */
static int borg_guess_race_name(cptr who)
{
	int k, m, n;

	int i, b_i = 0;
	int s, b_s = 0;

	monster_race *r_ptr;


	/* Start the search */
	m = 0; n = auto_unique_size;

	/* Binary search */
	while (m < n - 1)
	{
		/* Pick a "middle" entry */
		i = (m + n) / 2;

		/* Search to the right (or here) */
		if (strcmp(auto_unique_text[i], who) <= 0)
		{
			m = i;
		}

		/* Search to the left */
		else
		{
			n = i;
		}
	}

	/* Check for equality */
	if (streq(who, auto_unique_text[m]))
	{
		/* Use this monster */
		return (auto_unique_what[m]);
	}


	/* Assume player ghost */
	if (!prefix(who, "The "))
	{
		/* Message */
		borg_note(format("# Assuming player ghost (%s)", who));

		/* Oops */
		return (MAX_R_IDX-1);
	}

	/* Skip the prefix */
	who += 4;


	/* Start the search */
	m = 0; n = auto_normal_size;

	/* Binary search */
	while (m < n - 1)
	{
		/* Pick a "middle" entry */
		i = (m + n) / 2;

		/* Search to the right (or here) */
		if (strcmp(auto_normal_text[i], who) <= 0)
		{
			m = i;
		}

		/* Search to the left */
		else
		{
			n = i;
		}
	}

	/* Scan possibilities */
	for (k = m; k >= 0; k--)
	{
		/* Stop when done */
		if (!streq(who, auto_normal_text[k])) break;

		/* Extract the monster */
		i = auto_normal_what[k];

		/* Access the monster */
		r_ptr = &r_info[i];

		/* Basic score */
		s = 1000;

		/* Penalize "depth miss" */
		s = s - ABS(r_ptr->level - auto_depth);

		/* Track best */
		if (b_i && (s < b_s)) continue;

		/* Track it */
		b_i = i; b_s = s;
	}

	/* Success */
	if (b_i) return (b_i);


	/* Message */
	borg_note(format("# Assuming player ghost (%s)", who));

	/* Oops */
	return (MAX_R_IDX-1);
}


/*
 * Hack -- Update a "new" monster
 */
static void borg_update_kill_new(int i)
{
	auto_kill *kill = &auto_kills[i];

	monster_race *r_ptr = &r_info[kill->r_idx];


	/* Guess at the monster speed */
	kill->speed = (r_ptr->speed);

#if 0
	/* Hack -- assume optimal racial variety */
	if (!(r_ptr->flags1 & RF1_UNIQUE))
	{
		/* Hack -- Assume full speed bonus */
		kill->speed += (extract_energy[kill->speed] / 10);
	}
#endif


	/* Extract max hitpoints */
	kill->power = r_ptr->hdice * r_ptr->hside;


	/* Some monsters never sleep */
	if (r_ptr->sleep == 0) kill->awake = TRUE;

	/* Some monsters never move */
	if (r_ptr->flags1 & RF1_NEVER_MOVE) kill->awake = TRUE;
}


/*
 * Hack -- Update a "old" monster
 *
 * We round the player speed down, and the monster speed up,
 * and we assume maximum racial speed for each monster.
 */
static void borg_update_kill_old(int i)
{
	int t, e;

	auto_kill *kill = &auto_kills[i];

	/* Player energy per game turn */
	e = extract_energy[auto_speed];

	/* Game turns per player move */
	t = (100 + (e - 1)) / e;

	/* Monster energy per game turn */
	e = extract_energy[kill->speed];

	/* Monster moves (times ten) */
	kill->moves = (t * e) / 10;
}


/*
 * Delete an old "kill" record
 */
static void borg_delete_kill(int i)
{
	auto_kill *kill = &auto_kills[i];

	/* Paranoia -- Already wiped */
	if (!kill->r_idx) return;

	/* Note */
	borg_note(format("# Forgetting a monster '%s' at (%d,%d)",
	                 (r_name + r_info[kill->r_idx].name),
	                 kill->x, kill->y));

	/* Update the grids */
	auto_grids[kill->y][kill->x].kill = 0;

	/* Kill the monster */
	WIPE(kill, auto_kill);

	/* One less monster */
	auto_kills_cnt--;

	/* Recalculate danger */
	auto_danger_wipe = TRUE;

	/* Wipe goals */
	goal = 0;
}



/*
 * Determine if a monster should be "viewable"
 */
static bool borg_follow_kill_aux(int i, int y, int x)
{
	int d;

	auto_grid *ag;

	auto_kill *kill = &auto_kills[i];

	monster_race *r_ptr = &r_info[kill->r_idx];


	/* Distance to player */
	d = distance(c_y, c_x, y, x);

	/* Too far away */
	if (d > MAX_SIGHT) return (FALSE);


	/* Access the grid */
	ag = &auto_grids[y][x];

	/* Not on-screen */
	if (!(ag->info & BORG_OKAY)) return (FALSE);


	/* Line of sight */
	if (ag->info & BORG_VIEW)
	{
		/* Use "illumination" */
		if (ag->info & (BORG_LITE | BORG_GLOW))
		{
			/* We can see invisible */
			if (my_see_inv) return (TRUE);

			/* Monster is not invisible */
			if (!(r_ptr->flags2 & RF2_INVISIBLE)) return (TRUE);
		}

		/* Use "infravision" */
		if (d <= my_see_infra)
		{
			/* Infravision works on "warm" creatures */
			if (!(r_ptr->flags2 & RF2_COLD_BLOOD)) return (TRUE);
		}
	}


	/* Telepathy requires "telepathy" */
	if (my_telepathy)
	{
		/* Telepathy fails on "strange" monsters */
		if (r_ptr->flags2 & RF2_EMPTY_MIND) return (FALSE);
		if (r_ptr->flags2 & RF2_WEIRD_MIND) return (FALSE);

		/* Success */
		return (TRUE);
	}


	/* Nope */
	return (FALSE);
}


/*
 * Attempt to "follow" a missing monster
 *
 * This routine is not called when the player is blind or hallucinating.
 *
 * Currently this function is a total hack, but handles the case of only
 * one possible location (taking it), two adjacent possible locations
 * (taking the diagonal one), and three adjacent locations with the
 * central one being a diagonal (taking the diagonal one).
 *
 * We should perhaps handle the case of three adjacent locations with
 * the central one being a non-diagonal (taking the non-diagonal one).
 *
 * We should perhaps attempt to take into account "last known direction",
 * which would allow us to "predict" motion up to walls, and we should
 * perhaps attempt to take into account the "standard" flee algorithm,
 * though that feels a little like cheating.
 */
static void borg_follow_kill(int i)
{
	int j;
	int x, y;
	int ox, oy;

	int dx, b_dx = 0;
	int dy, b_dy = 0;

	auto_grid *ag;

	auto_kill *kill = &auto_kills[i];


	/* Paranoia */
	if (!kill->r_idx) return;


	/* Old location */
	ox = kill->x;
	oy = kill->y;


	/* Out of sight */
	if (!borg_follow_kill_aux(i, oy, ox)) return;


	/* Note */
	borg_note(format("# There was a monster '%s' at (%d,%d)",
	                 (r_name + r_info[kill->r_idx].name),
	                 ox, oy));


	/* Prevent silliness */
	if (!borg_cave_floor_bold(oy, ox))
	{
		/* Delete the monster */
		borg_delete_kill(i);

		/* Done */
		return;
	}

	/* Prevent loops */
	if (rand_int(100) < 1)
	{
		/* Just delete the monster */
		borg_delete_kill(i);

		/* Done */
		return;
	}


	/* Scan locations */
	for (j = 0; j < 8; j++)
	{
		/* Access offset */
		dx = ddx_ddd[j];
		dy = ddy_ddd[j];

		/* Access location */
		x = ox + dx;
		y = oy + dy;

		/* Access the grid */
		ag = &auto_grids[y][x];

		/* Skip known walls and doors */
		if (!borg_cave_floor_grid(ag)) continue;

		/* Skip known monsters */
		if (ag->kill) continue;

		/* Skip visible grids */
		if (borg_follow_kill_aux(i, y, x)) continue;

		/* Collect the offsets */
		b_dx += dx;
		b_dy += dy;
	}


	/* Don't go too far */
	if (b_dx < -1) b_dx = -1;
	else if (b_dx > 1) b_dx = 1;

	/* Don't go too far */
	if (b_dy < -1) b_dy = -1;
	else if (b_dy > 1) b_dy = 1;


	/* Access location */
	x = ox + b_dx;
	y = oy + b_dy;

	/* Access the grid */
	ag = &auto_grids[y][x];

	/* Avoid walls and doors */
	if (!borg_cave_floor_grid(ag))
	{
		/* Just delete the monster */
		borg_delete_kill(i);

		/* Done */
		return;
	}

	/* Avoid monsters */
	if (ag->kill)
	{
		/* Just delete the monster */
		borg_delete_kill(i);

		/* Done */
		return;
	}


	/* Assume awake */
	kill->awake = TRUE;

	/* Update the grids */
	auto_grids[kill->y][kill->x].kill = 0;

	/* Save the old Location */
	kill->ox = ox;
	kill->oy = oy;

	/* Save the Location */
	kill->x = ox + b_dx;
	kill->y = oy + b_dy;

	/* Update the grids */
	auto_grids[kill->y][kill->x].kill = i;

	/* Note */
	borg_note(format("# Following a monster '%s' to (%d,%d) from (%d,%d)",
	                 (r_name + r_info[kill->r_idx].name),
	                 kill->x, kill->y, ox, oy));

	/* Recalculate danger */
	auto_danger_wipe = TRUE;

	/* Clear goals */
	goal = 0;
}



/*
 * Obtain a new "kill" index
 */
static int borg_new_kill(int r_idx, int y, int x)
{
	int i, n = -1;

	auto_kill *kill;


	/* Look for a "dead" monster */
	for (i = 1; (n < 0) && (i < auto_kills_nxt); i++)
	{
		/* Skip real entries */
		if (!auto_kills[i].r_idx) n = i;
	}

	/* Allocate a new monster */
	if ((n < 0) && (auto_kills_nxt < 256))
	{
		/* Acquire the entry, advance */
		n = auto_kills_nxt++;
	}

	/* Hack -- steal an old monster */
	if (n < 0)
	{
		/* Note */
		borg_note("# Too many monsters");

		/* Hack -- Pick a random monster */
		n = rand_int(auto_kills_nxt-1) + 1;

		/* Kill it */
		borg_delete_kill(n);
	}


	/* Count the monsters */
	auto_kills_cnt++;

	/* Access the monster */
	kill = &auto_kills[n];

	/* Save the race */
	kill->r_idx = r_idx;

	/* Location */
	kill->ox = kill->x = x;
	kill->oy = kill->y = y;

	/* Update the grids */
	auto_grids[kill->y][kill->x].kill = n;

	/* Timestamp */
	kill->when = c_t;

	/* Update the monster */
	borg_update_kill_new(n);

	/* Update the monster */
	borg_update_kill_old(n);

	/* Recalculate danger */
	auto_danger_wipe = TRUE;

	/* Clear goals */
	goal = 0;

	/* Note */
	borg_note(format("# Creating a monster '%s' at (%d,%d)",
	                 (r_name + r_info[kill->r_idx].name),
	                 kill->x, kill->y));

	/* Wipe goals */
	goal = 0;

	/* Return the monster */
	return (n);
}



/*
 * Attempt to notice a changing "kill"
 */
static bool observe_kill_diff(int y, int x, byte a, char c)
{
	int i, r_idx;

	auto_kill *kill;

	/* Guess the race */
	r_idx = borg_guess_race(a, c, FALSE);

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

	/* Create a new monster */
	i = borg_new_kill(r_idx, y, x);

	/* Get the object */
	kill = &auto_kills[i];

	/* Timestamp */
	kill->when = c_t;

	/* Done */
	return (TRUE);
}


/*
 * Attempt to "track" a "kill" at the given location
 * Assume that the monster moved at most 'd' grids.
 * If "flag" is TRUE, allow monster "conversion"
 */
static bool observe_kill_move(int y, int x, int d, byte a, char c, bool flag)
{
	int i, z, ox, oy;

	int r_idx;

	auto_kill *kill;

	monster_race *r_ptr;


	/* Look at the monsters */
	for (i = 1; i < auto_kills_nxt; i++)
	{
		kill = &auto_kills[i];

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

		/* Skip assigned monsters */
		if (kill->seen) continue;

		/* Old location */
		ox = kill->x;
		oy = kill->y;

		/* Calculate distance */
		z = distance(oy, ox, y, x);

		/* Verify distance */
		if (z > d) continue;

		/* Verify "reasonable" motion, if allowed */
		if (!flag && (z > (kill->moves / 10) + 1)) continue;

		/* Access the monster race */
		r_ptr = &r_info[kill->r_idx];

		/* Verify matching char */
		if (c != r_ptr->x_char) continue;

		/* Verify matching attr */
		if (a != r_ptr->x_attr)
		{
			/* Require matching attr (for normal monsters) */
			if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR)))
			{
				/* Require flag */
				if (!flag) continue;

				/* Never flicker known monsters */
				if (kill->known) continue;

				/* Find a multi-hued monster */
				r_idx = borg_guess_race(a, c, TRUE);

				/* Handle failure */
				if (r_idx == MAX_R_IDX - 1) continue;

				/* Note */
				borg_note(format("# Flickering monster '%s' at (%d,%d)",
				                 (r_name + r_info[r_idx].name),
				                 x, y));
		
				/* Note */
				borg_note(format("# Converting a monster '%s' at (%d,%d)",
				                 (r_name + r_info[kill->r_idx].name),
				                 kill->x, kill->y));
		
				/* Change the race */
				kill->r_idx = r_idx;
		
				/* Update the monster */
				borg_update_kill_new(i);
		
				/* Update the monster */
				borg_update_kill_old(i);
		
				/* Recalculate danger */
				auto_danger_wipe = TRUE;
		
				/* Clear goals */
				goal = 0;
			}
		}

		/* Actual movement */
		if (z)
		{
			/* Assume awake */
			kill->awake = TRUE;

			/* Update the grids */
			auto_grids[kill->y][kill->x].kill = 0;

			/* Save the old Location */
			kill->ox = kill->x;
			kill->oy = kill->y;

			/* Save the Location */
			kill->x = x;
			kill->y = y;

			/* Update the grids */
			auto_grids[kill->y][kill->x].kill = i;

			/* Note */
			borg_note(format("# Tracking a monster '%s' at (%d,%d) from (%d,%d)",
			                 (r_name + r_ptr->name),
			                 kill->x, kill->y, ox, oy));

			/* Recalculate danger */
			auto_danger_wipe = TRUE;

			/* Clear goals */
			goal = 0;
		}

		/* Note when last seen */
		kill->when = c_t;

		/* Update the monster */
		borg_update_kill_old(i);

		/* Mark as seen */
		kill->seen = TRUE;

		/* Done */
		return (TRUE);
	}

	/* Oops */
	return (FALSE);
}



/*
 * Calculate base danger from a spell attack by an invisible monster
 *
 * We attempt to take account of various resistances, both in
 * terms of actual damage, and special effects, as appropriate.
 */
static int borg_fear_spell(int i)
{
	int z = 0;
	int p = 0;

	int ouch = 0;


	/* Damage taken */
	if (old_chp > auto_chp) ouch = (old_chp - auto_chp) * 2;


	/* Check the spell */
	switch (i)
	{
		case 96+0:    /* RF4_SHRIEK */
		p += 10;
		break;

		case 96+1:    /* RF4_XXX2X4 */
		break;

		case 96+2:    /* RF4_XXX3X4 */
		break;

		case 96+3:    /* RF4_XXX4X4 */
		break;

		case 96+4:    /* RF4_ARROW_1 */
		z = (1 * 6);
		break;

		case 96+5:    /* RF4_ARROW_2 */
		z = (3 * 6);
		break;

		case 96+6:    /* RF4_ARROW_3 */
		z = (5 * 6);
		break;

		case 96+7:    /* RF4_ARROW_4 */
		z = (7 * 6);
		break;

		case 96+8:    /* RF4_BR_ACID */
		if (my_immune_acid) break;
		z = ouch;
		p += 40;
		break;

		case 96+9:    /* RF4_BR_ELEC */
		if (my_immune_elec) break;
		z = ouch;
		p += 20;
		break;

		case 96+10:    /* RF4_BR_FIRE */
		if (my_immune_fire) break;
		z = ouch;
		p += 40;
		break;

		case 96+11:    /* RF4_BR_COLD */
		if (my_immune_cold) break;
		z = ouch;
		p += 20;
		break;

		case 96+12:    /* RF4_BR_POIS */
		z = ouch;
		if (my_resist_pois) break;
		p += 20;
		break;

		case 96+13:    /* RF4_BR_NETH */
		z = ouch;
		if (my_resist_nethr) break;
		p += 50;
		if (my_hold_life) break;
		p += 150;
		break;

		case 96+14:    /* RF4_BR_LITE */
		z = ouch;
		if (my_resist_lite) break;
		if (my_resist_blind) break;
		p += 20;
		break;

		case 96+15:    /* RF4_BR_DARK */
		z = ouch;
		if (my_resist_dark) break;
		if (my_resist_blind) break;
		p += 20;
		break;

		case 96+16:    /* RF4_BR_CONF */
		z = ouch;
		if (my_resist_confu) break;
		p += 100;
		break;

		case 96+17:    /* RF4_BR_SOUN */
		z = ouch;
		if (my_resist_sound) break;
		p += 50;
		break;

		case 96+18:    /* RF4_BR_CHAO */
		z = ouch;
		if (my_resist_chaos) break;
		p += 200;
		if (my_resist_nethr) break;
		if (my_hold_life) break;
		p += 100;
		if (my_resist_confu) break;
		p += 50;
		break;

		case 96+19:    /* RF4_BR_DISE */
		z = ouch;
		if (my_resist_disen) break;
		p += 500;
		break;

		case 96+20:    /* RF4_BR_NEXU */
		z = ouch;
		if (my_resist_nexus) break;
		p += 100;
		break;

		case 96+21:    /* RF4_BR_TIME */
		z = ouch;
		p += 200;
		break;

		case 96+22:    /* RF4_BR_INER */
		z = ouch;
		p += 50;
		break;

		case 96+23:    /* RF4_BR_GRAV */
		z = ouch;
		p += 50;
		if (my_resist_sound) break;
		p += 50;
		break;

		case 96+24:    /* RF4_BR_SHAR */
		z = ouch;
		if (my_resist_shard) break;
		p += 50;
		break;

		case 96+25:    /* RF4_BR_PLAS */
		z = ouch;
		if (my_resist_sound) break;
		p += 50;
		break;

		case 96+26:    /* RF4_BR_WALL */
		z = ouch;
		if (my_resist_sound) break;
		p += 50;
		break;

		case 96+27:    /* RF4_BR_MANA */
		/* XXX XXX XXX */
		break;

		case 96+28:    /* RF4_XXX5X4 */
		break;

		case 96+29:    /* RF4_XXX6X4 */
		break;

		case 96+30:    /* RF4_XXX7X4 */
		break;

		case 96+31:    /* RF4_XXX8X4 */
		break;



		case 128+0:    /* RF5_BA_ACID */
		if (my_immune_acid) break;
		z = ouch;
		p += 40;
		break;

		case 128+1:    /* RF5_BA_ELEC */
		if (my_immune_elec) break;
		z = ouch;
		p += 20;
		break;

		case 128+2:    /* RF5_BA_FIRE */
		if (my_immune_fire) break;
		z = ouch;
		p += 40;
		break;

		case 128+3:    /* RF5_BA_COLD */
		if (my_immune_cold) break;
		z = ouch;
		p += 20;
		break;

		case 128+4:    /* RF5_BA_POIS */
		z = (12 * 2);
		if (my_resist_pois) z = (z + 2) / 3;
		if (my_resist_pois) break;
		p += 20;
		break;

		case 128+5:    /* RF5_BA_NETH */
		z = ouch;
		if (my_resist_nethr) break;
		p += 200;
		break;

		case 128+6:    /* RF5_BA_WATE */
		z = ouch;
		p += 50;
		break;

		case 128+7:    /* RF5_BA_MANA */
		z = ouch;
		break;

		case 128+8:    /* RF5_BA_DARK */
		z = ouch;
		if (my_resist_dark) break;
		if (my_resist_blind) break;
		p += 20;
		break;

		case 128+9:    /* RF5_DRAIN_MANA */
		if (auto_msp) p += 10;
		break;

		case 128+10:    /* RF5_MIND_BLAST */
		z = 20;
		break;

		case 128+11:    /* RF5_BRAIN_SMASH */
		z = (12 * 15);
		p += 100;
		break;

		case 128+12:    /* RF5_CAUSE_1 */
		z = (3 * 8);
		break;

		case 128+13:    /* RF5_CAUSE_2 */
		z = (8 * 8);
		break;

		case 128+14:    /* RF5_CAUSE_3 */
		z = (10 * 15);
		break;

		case 128+15:    /* RF5_CAUSE_4 */
		z = (15 * 15);
		p += 50;
		break;

		case 128+16:    /* RF5_BO_ACID */
		if (my_immune_acid) break;
		z = ouch;
		p += 40;
		break;

		case 128+17:    /* RF5_BO_ELEC */
		if (my_immune_elec) break;
		z = ouch;
		p += 20;
		break;

		case 128+18:    /* RF5_BO_FIRE */
		if (my_immune_fire) break;
		z = ouch;
		p += 40;
		break;

		case 128+19:    /* RF5_BO_COLD */
		if (my_immune_cold) break;
		z = ouch;
		p += 20;
		break;

		case 128+20:    /* RF5_BO_POIS */
		/* XXX XXX XXX */
		break;

		case 128+21:    /* RF5_BO_NETH */
		z = ouch;
		if (my_resist_nethr) break;
		p += 200;
		break;

		case 128+22:    /* RF5_BO_WATE */
		z = ouch;
		p += 20;
		break;

		case 128+23:    /* RF5_BO_MANA */
		z = ouch;
		break;

		case 128+24:    /* RF5_BO_PLAS */
		z = ouch;
		p += 20;
		break;

		case 128+25:    /* RF5_BO_ICEE */
		z = ouch;
		p += 20;
		break;

		case 128+26:    /* RF5_MISSILE */
		z = ouch;
		break;

		case 128+27:    /* RF5_SCARE */
		p += 10;
		break;

		case 128+28:    /* RF5_BLIND */
		p += 10;
		break;

		case 128+29:    /* RF5_CONF */
		p += 10;
		break;

		case 128+30:    /* RF5_SLOW */
		p += 5;
		break;

		case 128+31:    /* RF5_HOLD */
		p += 20;
		break;



		case 160+0:    /* RF6_HASTE */
		p += 10;
		break;

		case 160+1:    /* RF6_XXX1X6 */
		break;

		case 160+2:    /* RF6_HEAL */
		p += 10;
		break;

		case 160+3:    /* RF6_XXX2X6 */
		break;

		case 160+4:    /* RF6_BLINK */
		break;

		case 160+5:    /* RF6_TPORT */
		break;

		case 160+6:    /* RF6_XXX3X6 */
		break;

		case 160+7:    /* RF6_XXX4X6 */
		break;

		case 160+8:    /* RF6_TELE_TO */
		p += 20;
		break;

		case 160+9:    /* RF6_TELE_AWAY */
		p += 10;
		break;

		case 160+10:    /* RF6_TELE_LEVEL */
		p += 50;
		break;

		case 160+11:    /* RF6_XXX5 */
		break;

		case 160+12:    /* RF6_DARKNESS */
		p += 5;
		break;

		case 160+13:    /* RF6_TRAPS */
		p += 50;
		break;

		case 160+14:    /* RF6_FORGET */
		p += 500;
		break;

		case 160+15:    /* RF6_XXX6X6 */
		break;

		case 160+16:    /* RF6_XXX7X6 */
		break;

		case 160+17:    /* RF6_XXX8X6 */
		break;

		case 160+18:    /* RF6_S_MONSTER */
		p += (auto_depth) * 10;
		break;

		case 160+19:    /* RF6_S_MONSTERS */
		p += (auto_depth) * 20;
		break;

		case 160+20:    /* RF6_S_ANT */
		p += (auto_depth) * 20;
		break;

		case 160+21:    /* RF6_S_SPIDER */
		p += (auto_depth) * 20;
		break;

		case 160+22:    /* RF6_S_HOUND */
		p += (auto_depth) * 20;
		break;

		case 160+23:    /* RF6_S_HYDRA */
		p += (auto_depth) * 20;
		break;

		case 160+24:    /* RF6_S_ANGEL */
		p += (auto_depth) * 30;
		break;

		case 160+25:    /* RF6_S_DEMON */
		p += (auto_depth) * 30;
		break;

		case 160+26:    /* RF6_S_UNDEAD */
		p += (auto_depth) * 30;
		break;

		case 160+27:    /* RF6_S_DRAGON */
		p += (auto_depth) * 30;
		break;

		case 160+28:    /* RF6_S_HI_UNDEAD */
		p += (auto_depth) * 50;
		break;

		case 160+29:    /* RF6_S_HI_DRAGON */
		p += (auto_depth) * 50;
		break;

		case 160+30:    /* RF6_S_WRAITH */
		p += (auto_depth) * 50;
		break;

		case 160+31:    /* RF6_S_UNIQUE */
		p += (auto_depth) * 50;
		break;
	}

	/* Notice damage */
	return (p + z);
}



/*
 * Increase the "region danger"
 */
static void borg_fear_grid(int y, int x, int k)
{
	int x0, y0, x1, x2, y1, y2;


	/* Message */
	borg_note(format("# Fearing grid (%d,%d) value %d", x, y, k));


	/* Current region */
	y0 = (y/11);
	x0 = (x/11);

	/* Nearby regions */
	y1 = (y0 > 0) ? (y0 - 1) : 0;
	x1 = (x0 > 0) ? (x0 - 1) : 0;
	y2 = (x0 < 5) ? (x0 + 1) : 5;
	x2 = (x0 < 17) ? (x0 + 1) : 17;


	/* Collect "fear", limit to 10000, spread around */
	auto_fear_region[y0][x0] = MIN(auto_fear_region[y0][x0] + k * 3, 10000);
	auto_fear_region[y0][x1] = MIN(auto_fear_region[y0][x1] + k * 2, 10000);
	auto_fear_region[y0][x2] = MIN(auto_fear_region[y0][x2] + k * 2, 10000);
	auto_fear_region[y1][x0] = MIN(auto_fear_region[y1][x0] + k * 2, 10000);
	auto_fear_region[y2][x0] = MIN(auto_fear_region[y2][x0] + k * 2, 10000);
	auto_fear_region[y1][x1] = MIN(auto_fear_region[y1][x1] + k * 1, 10000);
	auto_fear_region[y1][x2] = MIN(auto_fear_region[y1][x2] + k * 1, 10000);
	auto_fear_region[y2][x1] = MIN(auto_fear_region[y2][x1] + k * 1, 10000);
	auto_fear_region[y2][x2] = MIN(auto_fear_region[y2][x1] + k * 1, 10000);
}



/*
 * Attempt to locate a monster which could explain a message involving
 * the given monster name, near the given location, up to the given
 * distance from the given location.
 *
 * Invisible monsters, bizarre monsters, and unexplainable monsters are
 * all treated the same way, and should eventually contribute some amount
 * of basic "fear" to the current region.
 *
 * First, we attempt to convert "similar" objects into the desired monster,
 * then we attempt to convert "similar" monsters into the desired monster,
 * then we attempt to match an existing monster, and finally, we give up.
 *
 * XXX XXX XXX Hack -- To prevent fatal situations, every time we think
 * there may be a monster nearby, we look for a nearby object which could
 * be the indicated monster, and convert it into that monster.  This allows
 * us to correctly handle a room full of multiplying clear mushrooms.
 *
 * XXX XXX XXX When surrounded by multiple monsters of the same type,
 * we will ascribe most messages to one of those monsters, and ignore
 * the existance of all the other similar monsters.
 *
 * XXX XXX XXX Currently, confusion may cause messages to be ignored.
 */
static bool borg_locate_kill(cptr who, int y, int x, int r)
{
	int i, d, r_idx;

	int b_i, b_d;

	auto_take *take;
	auto_kill *kill;

	object_kind *k_ptr;

	monster_race *r_ptr;


	/* Handle invisible monsters */
	if (streq(who, "It") ||
	    streq(who, "Someone") ||
	    streq(who, "Something"))
	{
		/* Note */
		borg_note("# Invisible monster nearby");

		/* Ignore */
		return (0);
	}


	/* Guess the monster race */
	r_idx = borg_guess_race_name(who);

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

	/* Note */
	borg_note(format("# There is a monster '%s' within %d grids of %d,%d",
	                 (r_name + r_ptr->name),
	                 r, x, y));

	/* Hack -- count racial appearances */
	if (auto_race_count[r_idx] < MAX_SHORT) auto_race_count[r_idx]++;


	/* Handle trappers and lurkers and mimics */
	if (r_ptr->flags1 & (RF1_CHAR_CLEAR | RF1_CHAR_MULTI))
	{
		/* Note */
		borg_note("# Bizarre monster nearby");

		/* Ignore */
		return (0);
	}


	/*** Hack -- Find a similar object ***/

	/* Nothing yet */
	b_i = -1; b_d = 999;

	/* Scan the objects */
	for (i = 1; i < auto_takes_nxt; i++)
	{
		take = &auto_takes[i];

		/* Skip "dead" objects */
		if (!take->k_idx) continue;

		/* Access kind */
		k_ptr = &k_info[take->k_idx];

		/* Verify char */
		if (k_ptr->x_char != r_ptr->x_char) continue;

		/* Verify attr (unless clear or multi-hued) */
		if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR)))
		{
			/* Verify attr (unless flavored) */
			if (!(k_ptr->has_flavor))
			{
				/* Verify attr */
				if (k_ptr->x_attr != r_ptr->x_attr) continue;
			}
		}

		/* Calculate distance */
		d = distance(take->y, take->x, y, x);

		/* Skip "wrong" objects */
		if (d > r) continue;

		/* Track closest one */
		if (d > b_d) continue;

		/* Track it */
		b_i = i; b_d = d;
	}

	/* Found one */
	if (b_i >= 0)
	{
		take = &auto_takes[b_i];

		/* Access kind */
		k_ptr = &k_info[take->k_idx];

		/* Note */
		borg_note(format("# Converting an object '%s' at (%d,%d)",
		                 (k_name + k_ptr->name),
		                 take->x, take->y));

		/* Save location */
		x = take->x;
		y = take->y;

		/* Delete the object */
		borg_delete_take(b_i);

		/* Make a new monster */
		b_i = borg_new_kill(r_idx, y, x);

		/* Get the monster */
		kill = &auto_kills[b_i];

		/* Timestamp */
		kill->when = c_t;

		/* Known identity */
		if (!r) kill->known = TRUE;

		/* Assume awake */
		kill->awake = TRUE;

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


	/*** Hack -- Find a similar monster ***/

	/* Nothing yet */
	b_i = -1; b_d = 999;

	/* Scan the monsters */
	for (i = 1; i < auto_kills_nxt; i++)
	{
		kill = &auto_kills[i];

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

		/* Skip "matching" monsters */
		if (kill->r_idx == r_idx) continue;

		/* Verify char */
		if (r_info[kill->r_idx].x_char != r_ptr->x_char) continue;

		/* Verify attr (unless clear or multi-hued) */
		if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR)))
		{
			/* Verify attr */
			if (r_info[kill->r_idx].x_attr != r_ptr->x_attr) continue;
		}

		/* Distance away */
		d = distance(kill->y, kill->x, y, x);

		/* Check distance */
		if (d > r) continue;

		/* Track closest one */
		if (d > b_d) continue;

		/* Track it */
		b_i = i; b_d = d;
	}

	/* Found one */
	if (b_i >= 0)
	{
		kill = &auto_kills[b_i];

		/* Note */
		borg_note(format("# Converting a monster '%s' at (%d,%d)",
		                 (r_name + r_info[kill->r_idx].name),
		                 kill->x, kill->y));

		/* Change the race */
		kill->r_idx = r_idx;

		/* Update the monster */
		borg_update_kill_new(b_i);

		/* Update the monster */
		borg_update_kill_old(b_i);

		/* Known identity */
		if (!r) kill->known = TRUE;

		/* Wake the monster */
		kill->awake = TRUE;

		/* Recalculate danger */
		auto_danger_wipe = TRUE;

		/* Clear goals */
		goal = 0;

		/* Index */
		return (b_i);
	}


	/*** Hack -- Find an existing monster ***/

	/* Nothing yet */
	b_i = -1; b_d = 999;

	/* Scan the monsters */
	for (i = 1; i < auto_kills_nxt; i++)
	{
		kill = &auto_kills[i];

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

		/* Skip "different" monsters */
		if (kill->r_idx != r_idx) continue;

		/* Distance away */
		d = distance(kill->y, kill->x, y, x);

		/* Check distance */
		if (d > r) continue;

		/* Track closest one */
		if (d > b_d) continue;

		/* Track it */
		b_i = i; b_d = d;
	}

	/* Found one */
	if (b_i >= 0)
	{
		kill = &auto_kills[b_i];

		/* Note */
		borg_note(format("# Matched a monster '%s' at (%d,%d)",
		                 (r_name + r_info[kill->r_idx].name),
		                 kill->x, kill->y));

		/* Known identity */
		if (!r) kill->known = TRUE;

		/* Wake the monster */
		kill->awake = TRUE;

		/* Index */
		return (b_i);
	}


	/*** Oops ***/

	/* Note */
	borg_note(format("# Ignoring a monster '%s' near (%d,%d)",
	                 (r_name + r_ptr->name),
	                 x, y));

	/* Oops */
	return (0);
}




/*
 * Notice the "death" of a monster
 */
static void borg_count_death(int i)
{
	int r_idx;

	auto_kill *kill = &auto_kills[i];

	/* Access race */
	r_idx = kill->r_idx;

	/* Hack -- count racial deaths */
	if (auto_race_death[r_idx] < MAX_SHORT) auto_race_death[r_idx]++;
}




/*
 * Handle "detection" spells and "call lite"
 *
 * Note that we must use the "old" player location
 */
static bool borg_handle_self(cptr str)
{
	int i;

	int q_x, q_y;


	/* Extract panel */
	q_x = o_w_x / 33;
	q_y = o_w_y / 11;


	/* Handle failure */
	if (auto_failure)
	{
		borg_note("# Something failed");
	}

	/* Handle "call lite" */
	else if (prefix(str, "lite"))
	{
		/* Message */
		borg_note(format("# Called lite at (%d,%d)",
		                 o_c_x, o_c_y));

		/* Hack -- convert torch-lit grids to perma-lit grids */
		for (i = 0; i < auto_lite_n; i++)
		{
			int x = auto_lite_x[i];
			int y = auto_lite_y[i];

			/* Get the grid */
			auto_grid *ag = &auto_grids[y][x];

			/* Mark as perma-lit */
			ag->info |= BORG_GLOW;

			/* Mark as not dark */
			ag->info &= ~BORG_DARK;
		}
	}

	/* Handle "detect walls" */
	else if (prefix(str, "wall"))
	{
		/* Message */
		borg_note(format("# Detected walls (%d,%d to %d,%d)",
		                 q_x, q_y, q_x+1, q_y+1));

		/* Mark detected walls */
		auto_detect_wall[q_y+0][q_x+0] = TRUE;
		auto_detect_wall[q_y+0][q_x+1] = TRUE;
		auto_detect_wall[q_y+1][q_x+0] = TRUE;
		auto_detect_wall[q_y+1][q_x+1] = TRUE;
	}

	/* Handle "detect traps" */
	else if (prefix(str, "trap"))
	{
		/* Message */
		borg_note(format("# Detected traps (%d,%d to %d,%d)",
		                 q_x, q_y, q_x+1, q_y+1));

		/* Mark detected traps */
		auto_detect_trap[q_y+0][q_x+0] = TRUE;
		auto_detect_trap[q_y+0][q_x+1] = TRUE;
		auto_detect_trap[q_y+1][q_x+0] = TRUE;
		auto_detect_trap[q_y+1][q_x+1] = TRUE;
	}

	/* Handle "detect doors" */
	else if (prefix(str, "door"))
	{
		/* Message */
		borg_note(format("# Detected doors (%d,%d to %d,%d)",
		                 q_x, q_y, q_x+1, q_y+1));

		/* Mark detected doors */
		auto_detect_door[q_y+0][q_x+0] = TRUE;
		auto_detect_door[q_y+0][q_x+1] = TRUE;
		auto_detect_door[q_y+1][q_x+0] = TRUE;
		auto_detect_door[q_y+1][q_x+1] = TRUE;
	}

	/* Handle "detect traps and doors" */
	else if (prefix(str, "both"))
	{
		/* Message */
		borg_note(format("# Detected traps and doors (%d,%d to %d,%d)",
		                 q_x, q_y, q_x+1, q_y+1));

		/* Mark detected traps */
		auto_detect_trap[q_y+0][q_x+0] = TRUE;
		auto_detect_trap[q_y+0][q_x+1] = TRUE;
		auto_detect_trap[q_y+1][q_x+0] = TRUE;
		auto_detect_trap[q_y+1][q_x+1] = TRUE;

		/* Mark detected doors */
		auto_detect_door[q_y+0][q_x+0] = TRUE;
		auto_detect_door[q_y+0][q_x+1] = TRUE;
		auto_detect_door[q_y+1][q_x+0] = TRUE;
		auto_detect_door[q_y+1][q_x+1] = TRUE;
	}

	/* Done */
	return (TRUE);
}



/*
 * Update the Borg based on the current "map"
 */
static void borg_forget_map(void)
{
	int x, y;

	auto_grid *ag;


#ifdef BORG_ROOMS

	/* Wipe the "rooms" */
	borg_wipe_rooms();

#endif


	/* Clean up the grids */
	for (y = 0; y < AUTO_MAX_Y; y++)
	{
		for (x = 0; x < AUTO_MAX_X; x++)
		{
			/* Access the grid */
			ag = &auto_grids[y][x];

			/* Wipe it */
			WIPE(ag, auto_grid);

			/* Lay down the outer walls */
			ag->feat = FEAT_PERM_SOLID;
		}
	}

	/* Clean up the grids */
	for (y = 1; y < AUTO_MAX_Y-1; y++)
	{
		for (x = 1; x < AUTO_MAX_X-1; x++)
		{
			/* Access the grid */
			ag = &auto_grids[y][x];

			/* Forget the contents */
			ag->feat = FEAT_NONE;

			/* Hack -- prepare the town */
			if (!auto_depth) ag->feat = FEAT_FLOOR;
		}
	}


	/* Reset "auto_data_cost" */
	COPY(auto_data_cost, auto_data_hard, auto_data);

	/* Reset "auto_data_flow" */
	COPY(auto_data_flow, auto_data_hard, auto_data);


	/* Clear "auto_data_know" */
	WIPE(auto_data_know, auto_data);

	/* Clear "auto_data_icky" */
	WIPE(auto_data_icky, auto_data);


	/* Forget the view */
	borg_forget_view();
}



/*
 * Update the "map" based on visual info on the screen
 *
 * Note that we make assumptions about the grid under the player,
 * to prevent painful situations such as seeming to be standing
 * in a wall, or on a trap, etc.
 *
 * In general, we use the same "feat" codes as the game itself, but
 * sometimes we are just guessing (as with "visible traps"), and we
 * use some special codes, explained below.
 *
 * Note that we use the "feat" code of "FEAT_NONE" for grids which
 * have never been seen, or which, when seen, have always contained
 * an object or monster.  These grids are probably walls, unless
 * they contain a monster or object, in which case they are probably
 * floors, unless they contain a monster which passes through walls,
 * in which case they are probably walls.
 *
 * Note that we use the "feat" code of "FEAT_FLOOR" for grids which
 * were a normal floor last time we checked.  These grids may have
 * changed into non-floor grids recently (via earthquake?), unless
 * the grid is on the current panel, and is currently "lit" in some
 * manner, and does not contain a monster.
 *
 * Note that we use the "feat" code of "FEAT_INVIS" for grids which
 * once contained a wall/door, but then contained a monster or object.
 * These grids are probably floors, unless the grid contains a monster
 * which can pass through walls, in which case note that missiles and
 * spells may not affect a monster in the grid.
 *
 * Note that we use the other "feat" codes for grids which probably
 * contain the given feature type, unless several feature types use
 * the same symbol, in which case we use some "default" code, changing
 * our guess when messages provide us with more information.  This is
 * especially necessary for distinguishing magma from quartz, and for
 * distinguishing normal doors from locked doors from jammed doors.
 * Note that most "feat" codes, even if they are not "guesses", may
 * not be valid unless the grid is on the current panel.
 *
 * We use the "BORG_MARK" flag to mark a grid as having been "observed",
 * though this may or may not indicate that the "feature" code is known,
 * since observations of monsters or objects via telepathy and/or remote
 * detection may trigger this flag.
 *
 * We use the "BORG_OKAY" flag to mark a grid as being on the current
 * panel, which is used for various things, including verifying that
 * a grid can be used as the destination of a target, and to allow us
 * to assume that off-panel monsters are not "visible".
 *
 * Note the "interesting" code used to learn which floor grids are "dark"
 * and which are "perma-lit", by tracking those floor grids which appear
 * to be "lit", and then marking all of these grids which do not appear
 * to be lit by the torch as "known" to be illuminated, and by marking
 * any grids which "disappear" or which are displayed as "dark floors"
 * as "known" to be "dark".  This leaves many grids, especially those
 * lit by the torch, as being neither lit nor dark.
 *
 * The basic problem is that, especially with no special options set,
 * the player has very little direct information about which grids
 * are perma-lit, since all non-floor grids are memorized when they
 * are seen, and torch-lit floor grids look just like perma-lit
 * floor grids.  Also, monsters hide any object or feature in their
 * grid, and objects hide any feature in their grid, and objects are
 * memorized when they are seen, and monsters can be detected by a
 * variety of methods, including infravision and telepathy.
 *
 * So we ignore most non-floor grids, and we mark any floor grids which
 * are "known" to be perma-lit as "BORG_GLOW", and any which are "known"
 * to be dark as "BORG_DARK".  These flags are used for many purposes,
 * most importantly, to determine when "call lite" would be useful, and
 * to help determine when a monster is standing in a viewable perma-lit
 * grid, and should thus be "visible", and to determine when the player
 * has "lite", even though his torch has gone out.
 *
 * When a "call lite" spell succeeds, we mark the grids around the
 * player as "BORG_GLOW" and not "BORG_DARK", but we do not attempt
 * to "spread" the lite to the entire room, since, in general, it is
 * not possible to know what grids are in the "room".
 *
 * Note that we assume that normally, when the player steps onto
 * something, it disappears, and turns into a normal floor, unless
 * the player is stepping onto a grid which is normally "permanent"
 * (floors, stairs, store doors), in which case it does not change.
 *
 * Note that when we encounter a grid which blocks motion, but which
 * was previously thought to not block motion, we must be sure to
 * remove it from any "flow" which might be in progress, to prevent
 * nasty situations in which we attempt to flow into a wall grid
 * which was thought to be something else, like an unknown grid.
 *
 * XXX XXX XXX Running out of lite can induce fatal confusion.
 */
static void borg_update_map(void)
{
	int i, x, y, dx, dy;

	auto_grid *ag;

	byte t_a;
	byte t_c;


	/* Analyze the current map panel */
	for (dy = 0; dy < SCREEN_HGT; dy++)
	{
		/* Direct access XXX XXX XXX */
		byte *aa = &(Term->scr->a[dy+1][13]);
		char *cc = &(Term->scr->c[dy+1][13]);


		/* Scan the row */
		for (dx = 0; dx < SCREEN_WID; dx++)
		{
			bool old_wall;
			bool new_wall;


			/* Obtain the map location */
			x = w_x + dx;
			y = w_y + dy;


			/* Save contents */
			t_a = *aa++;
			t_c = *cc++;


			/* Get the auto_grid */
			ag = &auto_grids[y][x];

			/* Notice "on-screen" */
			ag->info |= BORG_OKAY;

			/* Notice "knowledge" */
			if (t_c != ' ') ag->info |= BORG_MARK;


			/* Notice the player */
			if (t_c == '@')
			{
				/* Memorize player location */
				c_x = x;
				c_y = y;

				/* Hack -- white */
				t_a = TERM_WHITE;

				/* Permanent stuff */
				switch (ag->feat)
				{
					/* Open doors */
					case FEAT_OPEN:
					case FEAT_BROKEN:
					t_c = '\'';
					break;

					/* Stairs */
					case FEAT_LESS:
					t_c = '<';
					break;

					/* Stairs */
					case FEAT_MORE:
					t_c = '>';
					break;

					/* Shops */
					case FEAT_SHOP_HEAD + 0x00:
					t_c = '1';
					break;

					case FEAT_SHOP_HEAD + 0x01:
					t_c = '2';
					break;

					case FEAT_SHOP_HEAD + 0x02:
					t_c = '3';
					break;

					case FEAT_SHOP_HEAD + 0x03:
					t_c = '4';
					break;

					case FEAT_SHOP_HEAD + 0x04:
					t_c = '5';
					break;

					case FEAT_SHOP_HEAD + 0x05:
					t_c = '6';
					break;

					case FEAT_SHOP_HEAD + 0x06:
					t_c = '7';
					break;

					case FEAT_SHOP_HEAD + 0x07:
					t_c = '8';
					break;

					default:
					t_c = '.';
					break;
				}
			}


			/* Save the old "wall" or "door" */
			old_wall = !borg_cave_floor_grid(ag);

			/* Analyze symbol */
			switch (t_c)
			{
				/* Darkness */
				case ' ':
				{
					/* The grid is not lit */
					ag->info &= ~BORG_GLOW;

					/* Known grids must be dark floors */
					if (ag->feat != FEAT_NONE) ag->info |= BORG_DARK;

					/* Done */
					break;
				}

				/* Floors */
				case '.':
				{
					/* Handle "blind" */
					if (do_blind)
					{
						/* Nothing */
					}

					/* Handle "dark" floors */
					else if (t_a == TERM_L_DARK)
					{
						/* Dark floor grid */
						ag->info |= BORG_DARK;
						ag->info &= ~BORG_GLOW;
					}

					/* Handle "lit" floors */
					else
					{
						/* Track "lit" floors */
						auto_temp_y[auto_temp_n] = y;
						auto_temp_x[auto_temp_n] = x;
						auto_temp_n++;
					}

					/* Known floor */
					ag->feat = FEAT_FLOOR;

					/* Done */
					break;
				}

				/* Open doors */
				case '\'':
				{
					/* Accept broken */
					if (ag->feat == FEAT_BROKEN) break;

					/* Assume normal */
					ag->feat = FEAT_OPEN;

					/* Done */
					break;
				}

				/* Walls */
				case '#':
				{
					/* Accept non-granite */
					if (ag->feat >= FEAT_WALL_EXTRA) break;

					/* Assume granite */
					ag->feat = FEAT_WALL_EXTRA;

					/* Done */
					break;
				}

				/* Seams */
				case '%':
				{
					/* Accept quartz */
					if (ag->feat == FEAT_QUARTZ) break;

					/* Assume magma */
					ag->feat = FEAT_MAGMA;

					/* Done */
					break;
				}

				/* Hidden */
				case '*':
				{
					/* Accept quartz */
					if (ag->feat == FEAT_QUARTZ_K) break;

					/* Assume magma */
					ag->feat = FEAT_MAGMA_K;

					/* Done */
					break;
				}

				/* Rubble */
				case ':':
				{
					/* Assume rubble */
					ag->feat = FEAT_RUBBLE;

					/* Done */
					break;
				}

				/* Doors */
				case '+':
				{
					/* Accept closed and locked and jammed doors */
					if ((ag->feat >= FEAT_DOOR_HEAD) && (ag->feat <= FEAT_DOOR_TAIL)) break;

					/* Assume easy */
					ag->feat = FEAT_DOOR_HEAD + 0x00;

					/* Done */
					break;
				}

				/* Traps */
				case '^':
				{
					/* Assume trap door */
					ag->feat = FEAT_TRAP_HEAD + 0x00;

					/* Done */
					break;
				}

				/* Up stairs */
				case '<':
				{
					/* Obvious */
					ag->feat = FEAT_LESS;

					/* Check for an existing "up stairs" */
					for (i = 0; i < track_less_num; i++)
					{
						/* Stop if we already new about these stairs */
						if ((track_less_x[i] == x) && (track_less_y[i] == y)) break;
					}

					/* Track the newly discovered "up stairs" */
					if ((i == track_less_num) && (i < track_less_size))
					{
						track_less_x[i] = x;
						track_less_y[i] = y;
						track_less_num++;
					}

					/* Done */
					break;
				}

				/* Down stairs */
				case '>':
				{
					/* Obvious */
					ag->feat = FEAT_MORE;

					/* Check for an existing "down stairs" */
					for (i = 0; i < track_more_num; i++)
					{
						/* We already knew about that one */
						if ((track_more_x[i] == x) && (track_more_y[i] == y)) break;
					}

					/* Track the newly discovered "down stairs" */
					if ((i == track_more_num) && (i < track_more_size))
					{
						track_more_x[i] = x;
						track_more_y[i] = y;
						track_more_num++;
					}

					/* Done */
					break;
				}

				/* Store doors */
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				{
					/* Shop type */
					i = D2I(t_c) - 1;

					/* Obvious */
					ag->feat = FEAT_SHOP_HEAD + i;

					/* Save new information */
					track_shop_x[i] = x;
					track_shop_y[i] = y;

					/* Done */
					break;
				}

				/* Monsters/Objects */
				default:
				{
					auto_wank *wank;

					/* Access next wank, advance */
					wank = &auto_wanks[auto_wank_num++];

					/* Save some information */
					wank->x = x;
					wank->y = y;
					wank->t_a = t_a;
					wank->t_c = t_c;
					wank->is_take = auto_is_take[(byte)(t_c)];
					wank->is_kill = auto_is_kill[(byte)(t_c)];

					/* Mark old wall/door grids as probable floor grids */
					if (!borg_cave_floor_grid(ag)) ag->feat = FEAT_INVIS;

					/* Done */
					break;
				}
			}

			/* Save the new "wall" or "door" */
			new_wall = !borg_cave_floor_grid(ag);

			/* Notice wall changes */
			if (old_wall != new_wall)
			{
				/* Remove this grid from any flow */
				if (new_wall) auto_data_flow->data[y][x] = 255;

				/* Remove this grid from any flow */
				auto_data_know->data[y][x] = FALSE;

				/* Remove this grid from any flow */
				auto_data_icky->data[y][x] = FALSE;

				/* Recalculate the view (if needed) */
				if (ag->info & BORG_VIEW) auto_update_view = TRUE;

				/* Recalculate the lite (if needed) */
				if (ag->info & BORG_LITE) auto_update_lite = TRUE;
			}
		}
	}
}



/*
 * Look at the screen and update the borg
 *
 * Uses the "panel" info (w_x, w_y) obtained earlier
 *
 * Note that all the "important" messages that occured after our last
 * action have been "queued" in a usable form.  We must attempt to use
 * these messages to update our knowledge about the world, keeping in
 * mind that the world may have changed in drastic ways.
 *
 * Note that the "c_t" variable corresponds *roughly* to player turns,
 * except that resting and "repeated" commands count as a single turn,
 * and "free" moves (including "illegal" moves, such as attempted moves
 * into walls, or tunneling into monsters) are counted as turns.
 *
 * Also note that "c_t" is not incremented until the Borg is about to
 * do something, so nothing ever has a time-stamp of the current time.
 *
 * We rely on the fact that all "perma-lit" grids are memorized when
 * they are seen, so any grid on the current panel that appears "dark"
 * must not be perma-lit.
 *
 * We rely on the fact that all "objects" are memorized when they are
 * seen, so any grid on the current panel that appears "dark" must not
 * have an object in it.  But it could have a monster which looks like
 * an object, but this is very rare.  XXX XXX XXX
 *
 * XXX XXX XXX The basic problem with timestamping the monsters
 * and objects is that we often get a message about a monster, and so
 * we want to timestamp it, but then we cannot use the timestamp to
 * indicate that the monster has not been "checked" yet.  Perhaps
 * we need to do something like give each monster a "moved" flag,
 * and clear all the flags to FALSE each turn before tracking. (?)
 *
 * Note that when two monsters of the same race are standing next to
 * each other, and they both move, such that the second monster ends
 * up where the first one began, we will incorrectly assume that the
 * first monster stayed still, and either the second monster moved
 * two spaces, or the second monster disappeared and a third monster
 * appeared, which is technically possible, if the first monster ate
 * the second, and then cloned the third.
 *
 * There is a problem with monsters which look like objects, namely,
 * they are assumed to be objects, and then if they leave line of
 * sight, they disappear, and the Borg assumes that they are gone,
 * when really they should be identified as monsters.
 *
 * XXX XXX Hack -- note the fast direct access to the screen.
 */
void borg_update(void)
{
	int i, k, x, y, dx, dy;

	cptr msg;

	cptr what;

	auto_grid *ag;

	bool reset = FALSE;


	/*** Process objects/monsters ***/

	/* Scan monsters */
	for (i = 1; i < auto_kills_nxt; i++)
	{
		auto_kill *kill = &auto_kills[i];

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

		/* Clear flags */
		kill->seen = FALSE;
		kill->used = FALSE;

		/* Skip recently seen monsters */
		if (c_t - kill->when < 2000) continue;

		/* Note */
		borg_note(format("# Expiring a monster '%s' (%d) at (%d,%d)",
		                 (r_name + r_info[kill->r_idx].name), kill->r_idx,
		                 kill->x, kill->y));

		/* Kill the monster */
		borg_delete_kill(i);
	}

	/* Scan objects */
	for (i = 1; i < auto_takes_nxt; i++)
	{
		auto_take *take = &auto_takes[i];

		/* Skip dead objects */
		if (!take->k_idx) continue;

		/* Clear flags */
		take->seen = FALSE;

		/* Skip recently seen objects */
		if (c_t - take->when < 2000) continue;

		/* Note */
		borg_note(format("# Expiring an object '%s' (%d) at (%d,%d)",
		                 (k_name + k_info[take->k_idx].name), take->k_idx,
		                 take->x, take->y));

		/* Kill the object */
		borg_delete_take(i);
	}


	/*** Handle messages ***/

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Note the message */
		borg_note(format("# %s (+)", msg));
	}

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Skip parsed messages */
		if (auto_msg_use[i]) continue;

		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Get the arguments */
		what = strchr(msg, ':') + 1;

		/* Hack -- Handle "SELF" info */
		if (prefix(msg, "SELF:"))
		{
			(void)borg_handle_self(what);
			auto_msg_use[i] = 1;
		}

		/* Handle "You feel..." */
		else if (prefix(msg, "FEELING:"))
		{
			auto_feeling = atoi(what);
			auto_msg_use[i] = 1;
		}
	}

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Skip parsed messages */
		if (auto_msg_use[i]) continue;

		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Get the arguments */
		what = strchr(msg, ':') + 1;

		/* Handle "You hit xxx." */
		if (prefix(msg, "HIT:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0)
			{
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "You miss xxx." */
		else if (prefix(msg, "MISS:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0)
			{
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "You have killed xxx." */
		else if (prefix(msg, "KILL:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0)
			{
				borg_count_death(k);
				borg_delete_kill(k);
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "xxx dies." */
		else if (prefix(msg, "DIED:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 3)) != 0)
			{
				borg_count_death(k);
				borg_delete_kill(k);
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "xxx screams in pain." */
		else if (prefix(msg, "PAIN:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 3)) != 0)
			{
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__FEAR:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0)
			{
				auto_msg_use[i] = 2;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__BOLD:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0)
			{
				auto_msg_use[i] = 2;
			}
		}
	}

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Skip parsed messages */
		if (auto_msg_use[i]) continue;

		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Get the arguments */
		what = strchr(msg, ':') + 1;

		/* Handle "You hit xxx." */
		if (prefix(msg, "HIT:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "You miss xxx." */
		else if (prefix(msg, "MISS:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "You have killed xxx." */
		else if (prefix(msg, "KILL:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0)
			{
				borg_count_death(k);
				borg_delete_kill(k);
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "xxx dies." */
		else if (prefix(msg, "DIED:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				borg_count_death(k);
				borg_delete_kill(k);
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "xxx screams in pain." */
		else if (prefix(msg, "PAIN:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "xxx hits you." */
		else if (prefix(msg, "HIT_BY:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 1)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "xxx misses you." */
		else if (prefix(msg, "MISS_BY:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 1)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE_SLEEP:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "awake" */
		else if (prefix(msg, "STATE_AWAKE:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__FEAR:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__BOLD:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}

		/* Hack -- Handle "spell" */
		else if (prefix(msg, "SPELL_"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0)
			{
				auto_msg_use[i] = 3;
			}
		}
	}


	/*** Handle new levels ***/

	/* Hack -- note new levels */
	if (old_depth != auto_depth)
	{
		/* Hack -- Restart the clock */
		c_t = 1000;

		/* When level was begun */
		auto_began = c_t;

		/* Not completed */
		auto_completed = FALSE;

		/* New danger thresh-hold */
		avoidance = auto_chp;

		/* Wipe the danger */
		auto_danger_wipe = TRUE;

		/* Update some stuff */
		auto_update_view = TRUE;
		auto_update_lite = TRUE;

		/* Examine the world */
		auto_do_inven = TRUE;
		auto_do_equip = TRUE;
		auto_do_spell = TRUE;
		auto_do_panel = TRUE;
		auto_do_frame = TRUE;

		/* Enable some functions */
		auto_do_crush_junk = TRUE;
		auto_do_crush_hole = TRUE;
		auto_do_crush_slow = TRUE;

		/* Mega-Hack -- Clear "call lite" stamp */
		when_call_lite = 0;

		/* Mega-Hack -- Clear "wizard lite" stamp */
		when_wizard_lite = 0;

		/* Mega-Hack -- Clear "detect traps" stamp */
		when_detect_traps = 0;

		/* Mega-Hack -- Clear "detect doors" stamp */
		when_detect_doors = 0;

		/* Mega-Hack -- Clear "detect walls" stamp */
		when_detect_walls = 0;

		/* Hack -- Clear "panel" flags */
		for (y = 0; y < 6; y++)
		{
			for (x = 0; x < 6; x++)
			{
				auto_detect_wall[y][x] = FALSE;
				auto_detect_trap[y][x] = FALSE;
				auto_detect_door[y][x] = FALSE;
			}
		}

		/* Hack -- Clear "fear" */
		for (y = 0; y < 6; y++)
		{
			for (x = 0; x < 18; x++)
			{
				auto_fear_region[y][x] = 0;
			}
		}

		/* Hack -- Clear "shop visit" stamps */
		for (i = 0; i < 8; i++) auto_shops[i].when = 0;

		/* No goal yet */
		goal = 0;

		/* Hack -- Clear "shop" goals */
		goal_shop = goal_ware = goal_item = -1;

		/* Do not use any stairs */
		stair_less = stair_more = FALSE;

		/* Hack -- cannot rise past town */
		if (!auto_depth) goal_rising = FALSE;

		/* Assume not leaving the level */
		goal_leaving = FALSE;

		/* Assume not fleeing the level */
		goal_fleeing = FALSE;

		/* Assume not ignoring monsters */
		goal_ignoring = FALSE;

		/* No known stairs */
		track_less_num = 0;
		track_more_num = 0;

		/* No objects here */
		auto_takes_cnt = 0;
		auto_takes_nxt = 1;

		/* Forget old objects */
		C_WIPE(auto_takes, 256, auto_take);

		/* No monsters here */
		auto_kills_cnt = 0;
		auto_kills_nxt = 1;

		/* Forget old monsters */
		C_WIPE(auto_kills, 256, auto_kill);

		/* Hack -- Forget race counters */
		C_WIPE(auto_race_count, MAX_R_IDX, s16b);

		/* Forget the map */
		borg_forget_map();

		/* Reset */
		reset = TRUE;

		/* Save new depth */
		old_depth = auto_depth;
	}

	/* Handle old level */
	else
	{
		/* Reduce fear over time */
		if (!(c_t % 10))
		{
			for (y = 0; y < 6; y++)
			{
				for (x = 0; x < 18; x++)
				{
					if (auto_fear_region[y][x]) auto_fear_region[y][x]--;
				}
			}
		}

		/* Handle changing map panel */
		if ((o_w_x != w_x) || (o_w_y != w_y))
		{
			/* Forget the previous map panel */
			for (dy = 0; dy < SCREEN_HGT; dy++)
			{
				for (dx = 0; dx < SCREEN_WID; dx++)
				{
					/* Access the actual location */
					x = o_w_x + dx;
					y = o_w_y + dy;

					/* Get the auto_grid */
					ag = &auto_grids[y][x];

					/* Clear the "okay" field */
					ag->info &= ~BORG_OKAY;
				}
			}
		}
	}


	/*** Update the map ***/

	/* Track floors */
	auto_temp_n = 0;

	/* Update the map */
	borg_update_map();

	/* Reset */
	if (reset)
	{
		/* Fake old panel */
		o_w_x = w_x;
		o_w_y = w_y;

		/* Fake old location */
		o_c_x = c_x;
		o_c_y = c_y;

		/* Fake goal location */
		g_x = c_x;
		g_y = c_y;
	}

	/* Player moved */
	if ((o_c_x != c_x) || (o_c_y != c_y))
	{
		/* Update view */
		auto_update_view = TRUE;

		/* Update lite */
		auto_update_lite = TRUE;
	}

	/* Update the view */
	if (auto_update_view)
	{
		/* Update the view */
		borg_update_view();

		/* Take note */
		auto_update_view = FALSE;
	}

	/* Update the lite */
	if (auto_update_lite)
	{
		/* Update the lite */
		borg_update_lite();

		/* Take note */
		auto_update_lite = FALSE;
	}

	/* Examine "lit" grids */
	for (i = 0; i < auto_temp_n; i++)
	{
		/* Get location */
		x = auto_temp_x[i];
		y = auto_temp_y[i];

		/* Get the auto_grid */
		ag = &auto_grids[y][x];

		/* Skip torch-lit grids */
		if (ag->info & BORG_LITE) continue;

		/* Assume not dark */
		ag->info &= ~BORG_DARK;

		/* Assume perma-lit */
		ag->info |= BORG_GLOW;
	}


	/*** Track objects and monsters ***/

	/* Pass 1 -- stationary monsters */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track stationary monsters */
		if (wank->is_kill &&
		    observe_kill_move(wank->y, wank->x, 0, wank->t_a, wank->t_c, FALSE))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 2 -- stationary objects */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track stationary objects */
		if (wank->is_take &&
		    observe_take_move(wank->y, wank->x, 0, wank->t_a, wank->t_c))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 3a -- moving monsters (distance 1) */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track moving monsters */
		if (wank->is_kill &&
		    observe_kill_move(wank->y, wank->x, 1, wank->t_a, wank->t_c, FALSE))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 3b -- moving monsters (distance 2) */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track moving monsters */
		if (wank->is_kill &&
		    observe_kill_move(wank->y, wank->x, 2, wank->t_a, wank->t_c, FALSE))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 3c -- moving monsters (distance 3) */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track moving monsters */
		if (wank->is_kill &&
		    observe_kill_move(wank->y, wank->x, 3, wank->t_a, wank->t_c, FALSE))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 3d -- moving monsters (distance 3, allow changes) */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track moving monsters */
		if (wank->is_kill &&
		    observe_kill_move(wank->y, wank->x, 3, wank->t_a, wank->t_c, TRUE))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 4 -- new objects */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track new objects */
		if (wank->is_take &&
		    observe_take_diff(wank->y, wank->x, wank->t_a, wank->t_c))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}

	/* Pass 5 -- new monsters */
	for (i = auto_wank_num - 1; i >= 0; i--)
	{
		auto_wank *wank = &auto_wanks[i];

		/* Track new monsters */
		if (wank->is_kill &&
		    observe_kill_diff(wank->y, wank->x, wank->t_a, wank->t_c))
		{
			/* Hack -- excise the entry */
			auto_wanks[i] = auto_wanks[--auto_wank_num];
		}
	}


	/*** Handle messages ***/

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Skip parsed messages */
		if (auto_msg_use[i]) continue;

		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Get the arguments */
		what = strchr(msg, ':') + 1;

		/* Handle "xxx dies." */
		if (prefix(msg, "DIED:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				borg_count_death(k);
				borg_delete_kill(k);
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "xxx screams in pain." */
		else if (prefix(msg, "PAIN:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "xxx hits you." */
		else if (prefix(msg, "HIT_BY:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 1)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "xxx misses you." */
		else if (prefix(msg, "MISS_BY:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 1)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE_SLEEP:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "awake" */
		else if (prefix(msg, "STATE_AWAKE:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__FEAR:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Handle "sleep" */
		else if (prefix(msg, "STATE__BOLD:"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}

		/* Hack -- Handle "spell" */
		else if (prefix(msg, "SPELL_"))
		{
			/* Attempt to find the monster */
			if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0)
			{
				auto_msg_use[i] = 4;
			}
		}
	}

	/* Process messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Skip parsed messages */
		if (auto_msg_use[i]) continue;

		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Get the arguments */
		what = strchr(msg, ':') + 1;

		/* Handle "xxx hits you." */
		if (prefix(msg, "HIT_BY:"))
		{
			borg_fear_grid(c_y, c_x, 4 * ((auto_depth / 5) + 1));
			auto_msg_use[i] = 5;
		}

		/* Handle "xxx misses you." */
		else if (prefix(msg, "MISS_BY:"))
		{
			borg_fear_grid(c_y, c_x, 2 * ((auto_depth / 5) + 1));
			auto_msg_use[i] = 5;
		}

		/* Hack -- Handle "spell" */
		else if (prefix(msg, "SPELL_"))
		{
			borg_fear_grid(c_y, c_x, borg_fear_spell(atoi(msg+6)));
			auto_msg_use[i] = 5;
		}
	}

	/* Display messages */
	for (i = 0; i < auto_msg_num; i++)
	{
		/* Get the message */
		msg = auto_msg_buf + auto_msg_pos[i];

		/* Final message */
		borg_note(format("# %s (%d)", msg, auto_msg_use[i]));
	}


	/*** Notice missing monsters ***/

	/* Scan the monster list */
	for (i = 1; i < auto_kills_nxt; i++)
	{
		auto_kill *kill = &auto_kills[i];

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

		/* Skip seen monsters */
		if (kill->when == c_t) continue;

		/* Hack -- blind or hallucinating */
		if (do_blind || do_image) continue;

		/* Predict the monster */
		borg_follow_kill(i);
	}


	/*** Notice missing objects ***/

	/* Scan the object list */
	for (i = 1; i < auto_takes_nxt; i++)
	{
		auto_take *take = &auto_takes[i];

		/* Skip dead objects */
		if (!take->k_idx) continue;

		/* Skip seen objects */
		if (take->when == c_t) continue;

		/* Hack -- blind or hallucinating */
		if (do_blind || do_image) continue;

		/* Follow the object */
		borg_follow_take(i);
	}


#ifdef BORG_ROOMS

	/* Analyze the current map panel */
	for (dy = 0; dy < SCREEN_HGT; dy++)
	{
		for (dx = 0; dx < SCREEN_WID; dx++)
		{
			/* Obtain the map location */
			x = w_x + dx;
			y = w_y + dy;

			/* Get the auto_grid */
			ag = &auto_grids[y][x];

			/* Clear all "broken" rooms */
			if (!borg_cave_floor_grid(ag))
			{
				/* Clear all rooms containing walls */
				if (ag->room)
				{
					/* Clear all rooms containing walls */
					if (borg_clear_room(y, x)) goal = 0;
				}
			}

			/* Create "fake" rooms as needed */
			else
			{
				/* Mega-Hack -- super-fake rooms */
				if (!ag->room)
				{
					/* Acquire a new room */
					auto_room *ar = borg_free_room();

					/* Initialize the room */
					ar->x1 = ar->x2 = x;
					ar->y1 = ar->y2 = y;

					/* Save the room */
					ag->room = ar->self;
				}
			}
		}
	}


	/*** Maintain room info ***/

	/* Make "fake" rooms for all viewable unknown grids */
	for (n = 0; n < auto_view_n; n++)
	{
		/* Access the location */
		y = auto_view_y[n];
		x = auto_view_x[n];

		/* Access the grid */
		ag = &auto_grids[y][x];

		/* Skip walls/doors */
		if (!borg_cave_floor_grid(ag)) continue;

		/* Skip "known" grids */
		if (ag->feat != FEAT_NONE) continue;

		/* Make "fake" rooms as needed */
		if (!ag->room)
		{
			auto_room *ar;

			/* Get a new room */
			ar = borg_free_room();

			/* Initialize the room */
			ar->x1 = ar->x2 = x;
			ar->y1 = ar->y2 = y;

			/* Save the room */
			ag->room = ar->self;
		}
	}

	/* Access the player grid */
	ag = &auto_grids[c_y][c_x];

	/* Paranoia -- require a self room */
	if (!ag->room)
	{
		/* Acquire a new room */
		auto_room *ar = borg_free_room();

		/* Initialize the room */
		ar->x1 = ar->x2 = c_x;
		ar->y1 = ar->y2 = c_y;

		/* Save the room */
		ag->room = ar->self;
	}

	/* Build a "bigger" room around the player */
	if (borg_build_room(c_y, c_x)) goal = 0;

	/* Hack */
	if (TRUE)
	{
		auto_room *ar;

		/* Mark all the "containing rooms" as visited. */
		for (ar = room(1, c_y, c_x); ar; ar = room(0, 0, 0))
		{
			/* Note the visit */
			ar->when = c_t;
		}
	}

#endif


	/*** Various things ***/

	/* Forget goals while "impaired" in any way */
	if (do_blind || do_confused || do_afraid || do_image) goal = 0;

	/* Forget goals while "bleeding" in any way */
	if (do_weak || do_poisoned || do_cut || do_stun) goal = 0;

	/* Forget goals when HP or SP changes */
	if ((auto_chp != old_chp) || (auto_csp != old_csp)) goal = 0;

	/* Save the hit points */
	old_chp = auto_chp;

	/* Save the spell points */
	old_csp = auto_csp;

	/* Forget failure */
	auto_failure = FALSE;

	/* Forget the messages */
	auto_msg_len = 0;
	auto_msg_num = 0;


	/*** Save old info ***/

	/* Save the old "location" */
	o_c_x = c_x;
	o_c_y = c_y;

	/* Save the old "panel" */
	o_w_x = w_x;
	o_w_y = w_y;


	/*** Defaults ***/

	/* Default "goal" location */
	g_x = c_x;
	g_y = c_y;
}


/*
 * Handle various "important" messages
 *
 * Actually, we simply "queue" them for later analysis
 */
void borg_react(cptr msg, cptr buf)
{
	int len;

	/* Note actual message */
	borg_note(format("> %s", msg));

	/* Extract length of parsed message */
	len = strlen(buf);

	/* Verify space */
	if (auto_msg_num + 1 > auto_msg_max)
	{
		borg_oops("too many messages");
		return;
	}

	/* Verify space */
	if (auto_msg_len + len + 1 > auto_msg_siz)
	{
		borg_oops("too much messages");
		return;
	}

	/* Assume not used yet */
	auto_msg_use[auto_msg_num] = 0;

	/* Save the message position */
	auto_msg_pos[auto_msg_num] = auto_msg_len;

	/* Save the message text */
	strcpy(auto_msg_buf + auto_msg_len, buf);

	/* Advance the buf */
	auto_msg_len += len + 1;

	/* Advance the pos */
	auto_msg_num++;
}



/*
 * Sorting hook -- comp function -- see below
 *
 * We use "u" to point to an array of strings, and "v" to point to
 * an array of indexes, and we sort them together by the strings.
 */
static bool ang_sort_comp_hook(vptr u, vptr v, int a, int b)
{
	cptr *text = (cptr*)(u);
	s16b *what = (s16b*)(v);

	int cmp;

	/* Compare the two strings */
	cmp = (strcmp(text[a], text[b]));

	/* Strictly less */
	if (cmp < 0) return (TRUE);

	/* Strictly more */
	if (cmp > 0) return (FALSE);

	/* Enforce "stable" sort */
	return (what[a] <= what[b]);
}


/*
 * Sorting hook -- swap function -- see below
 *
 * We use "u" to point to an array of strings, and "v" to point to
 * an array of indexes, and we sort them together by the strings.
 */
static void ang_sort_swap_hook(vptr u, vptr v, int a, int b)
{
	cptr *text = (cptr*)(u);
	s16b *what = (s16b*)(v);

	cptr texttmp;
	s16b whattmp;

	/* Swap "text" */
	texttmp = text[a];
	text[a] = text[b];
	text[b] = texttmp;

	/* Swap "what" */
	whattmp = what[a];
	what[a] = what[b];
	what[b] = whattmp;
}



/*
 * Initialize this file
 */
void borg_init_5(void)
{
	int i;

	int size;

	s16b what[512];
	cptr text[512];


	/*** Message tracking ***/

	/* No chars saved yet */
	auto_msg_len = 0;

	/* Maximum buffer size */
	auto_msg_siz = 4096;

	/* Allocate a buffer */
	C_MAKE(auto_msg_buf, auto_msg_siz, char);

	/* No msg's saved yet */
	auto_msg_num = 0;

	/* Maximum number of messages */
	auto_msg_max = 128;

	/* Allocate array of positions */
	C_MAKE(auto_msg_pos, auto_msg_max, s16b);

	/* Allocate array of use-types */
	C_MAKE(auto_msg_use, auto_msg_max, s16b);


	/*** Object/Monster tracking ***/

	/* Array of "wanks" */
	C_MAKE(auto_wanks, AUTO_VIEW_MAX, auto_wank);


	/*** Reset the map ***/

	/* Forget the map */
	borg_forget_map();


	/*** Parse "unique" monster names ***/

	/* Start over */
	size = 0;

	/* Collect "unique" monsters */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Skip non-unique monsters */
		if (!(r_ptr->flags1 & RF1_UNIQUE)) continue;

		/* Use it */
		text[size] = r_name + r_ptr->name;
		what[size] = i;
		size++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, size);

	/* Save the size */
	auto_unique_size = size;

	/* Allocate the arrays */
	C_MAKE(auto_unique_text, auto_unique_size, cptr);
	C_MAKE(auto_unique_what, auto_unique_size, s16b);

	/* Save the entries */
	for (i = 0; i < size; i++) auto_unique_text[i] = text[i];
	for (i = 0; i < size; i++) auto_unique_what[i] = what[i];


	/*** Parse "normal" monster names ***/

	/* Start over */
	size = 0;

	/* Collect "normal" monsters */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Skip unique monsters */
		if (r_ptr->flags1 & RF1_UNIQUE) continue;

		/* Use it */
		text[size] = r_name + r_ptr->name;
		what[size] = i;
		size++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, size);

	/* Save the size */
	auto_normal_size = size;

	/* Allocate the arrays */
	C_MAKE(auto_normal_text, auto_normal_size, cptr);
	C_MAKE(auto_normal_what, auto_normal_size, s16b);

	/* Save the entries */
	for (i = 0; i < size; i++) auto_normal_text[i] = text[i];
	for (i = 0; i < size; i++) auto_normal_what[i] = what[i];
}


#else

#ifdef MACINTOSH
static int HACK = 0;
#endif /* MACINTOSH */

#endif /* ALLOW_BORG */
