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

#include "xtra2.hpp"

#include "artifact_type.hpp"
#include "cave.hpp"
#include "cave_type.hpp"
#include "corrupt.hpp"
#include "dungeon_info_type.hpp"
#include "ego_item_type.hpp"
#include "feature_flag.hpp"
#include "feature_type.hpp"
#include "files.hpp"
#include "game.hpp"
#include "gods.hpp"
#include "hook_player_level_in.hpp"
#include "hook_monster_death_in.hpp"
#include "hooks.hpp"
#include "melee2.hpp"
#include "mimic.hpp"
#include "monster1.hpp"
#include "monster2.hpp"
#include "monster3.hpp"
#include "monster_ego.hpp"
#include "monster_race.hpp"
#include "monster_race_flag.hpp"
#include "monster_type.hpp"
#include "notes.hpp"
#include "object1.hpp"
#include "object2.hpp"
#include "object_flag.hpp"
#include "object_kind.hpp"
#include "options.hpp"
#include "player_class.hpp"
#include "player_race.hpp"
#include "player_race_flag.hpp"
#include "player_race_mod.hpp"
#include "player_type.hpp"
#include "point.hpp"
#include "randart.hpp"
#include "skill_type.hpp"
#include "skills.hpp"
#include "spells1.hpp"
#include "spells2.hpp"
#include "stats.hpp"
#include "store_info_type.hpp"
#include "tables.hpp"
#include "util.hpp"
#include "variable.hpp"
#include "wilderness_map.hpp"
#include "wilderness_type_info.hpp"
#include "wizard2.hpp"
#include "xtra1.hpp"
#include "z-rand.hpp"

#include <boost/algorithm/string/predicate.hpp>
#include <cassert>
#include <fmt/format.h>
#include <type_traits>

using boost::algorithm::iequals;
using boost::algorithm::starts_with;

static void corrupt_corrupted();

/*
 * Set "p_ptr->parasite" and "p_ptr->parasite_r_idx"
 * notice observable changes
 */
bool set_parasite(int v, int r)
{
	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!p_ptr->parasite)
		{
			msg_print("You feel something growing in you.");
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (p_ptr->parasite)
		{
			if (magik(80))
			{
				char r_name[80];
				int wx, wy;
				int attempts = 500;

				monster_race_desc(r_name, p_ptr->parasite_r_idx, 0);

				do
				{
					scatter(&wy, &wx, p_ptr->py, p_ptr->px, 10);
				}
				while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);

				if (place_monster_one(wy, wx, p_ptr->parasite_r_idx, 0, false, MSTATUS_ENEMY))
				{
					cmsg_format(TERM_L_BLUE, "Your body convulses and spawns %s.", r_name);
					p_ptr->food -= 750;
					if (p_ptr->food < 100) p_ptr->food = 100;
				}
			}
			else
			{
				cmsg_print(TERM_L_BLUE, "The hideous thing growing in you seems to die.");
			}
			notice = true;
		}
	}

	/* Use the value */
	p_ptr->parasite = v;
	p_ptr->parasite_r_idx = r;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Result */
	return true;
}

/*
 * Set a simple player field.
 */
static bool set_simple_field(
	s16b *p_field,
	s16b v,
	byte activate_color,
	const char *activate_msg,
	byte deactivate_color,
	const char *deactivate_msg)
{
	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!*p_field)
		{
			cmsg_print(activate_color, activate_msg);
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (*p_field)
		{
			cmsg_print(deactivate_color, deactivate_msg);
			notice = true;
		}
	}

	/* Use the value */
	*p_field = v;

	/* Nothing to notice */
	if (!notice)
		return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Result */
	return true;
}

/*
 * Set "p_ptr->tim_project" and others
 * notice observable changes
 */
bool set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
{
	bool notice = set_simple_field(
		&p_ptr->tim_project, v,
		TERM_WHITE, "Your weapon starts glowing.",
		TERM_WHITE, "Your weapon stops glowing.");

	/* Use the values */
	p_ptr->tim_project_gf = gf;
	p_ptr->tim_project_dam = dam;
	p_ptr->tim_project_rad = rad;
	p_ptr->tim_project_flag = flag;

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->tim_roots" and others
 * notice observable changes
 */
bool set_roots(int v, s16b ac, s16b dam)
{
	bool notice = set_simple_field(
		&p_ptr->tim_roots, v,
		TERM_WHITE, "Roots dive into the floor from your feet.",
		TERM_WHITE, "The roots of your feet suddenly vanish.");

	/* Use the values */
	p_ptr->tim_roots_dam = dam;
	p_ptr->tim_roots_ac = ac;

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->tim_(magic|water)_breath" and others
 * notice observable changes
 */
bool set_tim_breath(int v, bool magical)
{
	if (magical)
	{
		return set_simple_field(
			&p_ptr->tim_magic_breath, v,
			TERM_WHITE, "Air seems to fill your lungs without breathing.",
			TERM_WHITE, "You need to breathe again.");
	}
	else
	{
		return set_simple_field(
			&p_ptr->tim_water_breath, v,
			TERM_WHITE, "Water seems to fill your lungs.",
			TERM_WHITE, "The water filling your lungs evaporates.");
	}
}

/*
 * Set timered precognition
 */
bool set_tim_precognition(int v)
{
	return set_simple_field(
		&p_ptr->tim_precognition, v,
		TERM_WHITE, "You feel able to predict the future.",
		TERM_WHITE, "You feel less able to predict the future.");
}

/*
 * Set "p_ptr->absorb_soul"
 * notice observable changes
 */
bool set_absorb_soul(int v)
{
	return set_simple_field(
		&p_ptr->absorb_soul, v,
		TERM_L_DARK, "You start absorbing the souls of your foes.",
		TERM_L_DARK, "You stop absorbing the souls of dead foes.");
}

/*
 * Set "p_ptr->disrupt_shield"
 * notice observable changes
 */
bool set_disrupt_shield(int v)
{
	return set_simple_field(
		&p_ptr->disrupt_shield, v,
		TERM_L_BLUE, "You feel invulnerable.",
		TERM_L_RED, "You are more vulnerable.");
}

/*
 * Set "p_ptr->prob_travel"
 * notice observable changes
 */
bool set_prob_travel(int v)
{
	return set_simple_field(
		&p_ptr->prob_travel, v,
		TERM_WHITE, "You feel instable.",
		TERM_WHITE, "You are more stable.");
}

/*
 * Set "p_ptr->tim_invis", and "p_ptr->tim_inv_pow",
 * notice observable changes
 */
bool set_invis(int v, int p)
{
	bool notice = set_simple_field(
		&p_ptr->tim_invisible, v,
		TERM_WHITE, "You feel your body fade away.",
		TERM_WHITE, "You are no longer invisible.");

	/* Use the power value */
	p_ptr->tim_inv_pow = p;

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->tim_poison",
 * notice observable changes
 */
bool set_poison(int v)
{
	return set_simple_field(
		&p_ptr->tim_poison, v,
		TERM_WHITE, "Your hands are dripping with venom.",
		TERM_WHITE, "The venom source dries out.");
}

/*
 * Set "no_breeds"
 */
bool set_no_breeders(int v)
{
	return set_simple_field(
		&no_breeds, v,
		TERM_WHITE, "You feel an anti-sexual aura.",
		TERM_WHITE, "You no longer feel an anti-sexual aura.");
}

/*
 * Set "p_ptr->tim_deadly"
 */
bool set_tim_deadly(int v)
{
	return set_simple_field(
		&p_ptr->tim_deadly, v,
		TERM_WHITE, "You feel extremely accurate.",
		TERM_WHITE, "You are suddenly much less accurate.");
}

/*
 * Set "p_ptr->tim_ffall"
 */
bool set_tim_ffall(int v)
{
	return set_simple_field(
		&p_ptr->tim_ffall, v,
		TERM_WHITE, "You feel very light.",
		TERM_WHITE, "You are suddenly heavier.");
}

/*
 * Set "p_ptr->tim_fly"
 */
bool set_tim_fly(int v)
{
	return set_simple_field(
		&p_ptr->tim_fly, v,
		TERM_WHITE, "You feel able to reach the clouds.",
		TERM_WHITE, "You are suddenly a lot heavier.");
}

/*
 * Set "p_ptr->tim_reflect"
 */
bool set_tim_reflect(int v)
{
	return set_simple_field(
		&p_ptr->tim_reflect, v,
		TERM_WHITE, "You start reflecting the world around you.",
		TERM_WHITE, "You stop reflecting.");
}

/*
 * Set "p_ptr->strike"
 */
bool set_strike(int v)
{
	return set_simple_field(
		&p_ptr->strike, v,
		TERM_WHITE, "You feel very accurate.",
		TERM_WHITE, "You are no longer very accurate.");
}

/*
 * Set "p_ptr->oppose_cc"
 */
bool set_oppose_cc(int v)
{
	return set_simple_field(
		&p_ptr->oppose_cc, v,
		TERM_WHITE, "You feel protected against raw chaos.",
		TERM_WHITE, "You are no longer protected against chaos.");
}

/*
 * Set "p_ptr->tim_mimic", and "p_ptr->mimic_form",
 * notice observable changes
 */
bool set_mimic(int v, int p, int level)
{
	auto &s_info = game->s_info;

	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!p_ptr->tim_mimic)
		{
			msg_print("You feel your body change.");
			p_ptr->mimic_form = p;
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (p_ptr->tim_mimic)
		{
			msg_print("You are no longer transformed.");
			p_ptr->mimic_form = 0;
			notice = true;
			if (p == resolve_mimic_name("Bear"))
			{
				s_info[SKILL_BEAR].hidden = true;
				select_default_melee();
			}
			p = 0;
		}
	}

	/* Use the value */
	p_ptr->tim_mimic = v;
	p_ptr->mimic_level = level;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Redraw title */
	p_ptr->redraw |= (PR_FRAME);

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BODY | PU_BONUS | PU_SANITY);

	/* Result */
	return true;
}

/*
 * Set "p_ptr->blind", notice observable changes
 *
 * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
 * features which suddenly become "visible".
 * Note that blindness is currently the only thing which can affect
 * "player_can_see_bold()".
 */
bool set_blind(int v)
{
	bool notice = set_simple_field(
		&p_ptr->blind, v,
		TERM_WHITE, "You are blind!",
		TERM_WHITE, "You can see again.");

	if (notice)
	{
		/* Fully update the visuals */
		p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);

		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Redraw the "blind" */
		p_ptr->redraw |= (PR_FRAME);

		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->tim_lite", notice observable changes
 *
 * Note the use of "PU_VIEW", which is needed to
 * memorize any terrain features which suddenly become "visible".
 * Note that blindness is currently the only thing which can affect
 * "player_can_see_bold()".
 */
bool set_lite(int v)
{
	bool notice = set_simple_field(
		&p_ptr->tim_lite, v,
		TERM_WHITE, "You suddenly seem brighter!",
		TERM_WHITE, "You are no longer bright.");

	if (notice)
	{
		/* Fully update the visuals */
		p_ptr->update |= (PU_VIEW | PU_MONSTERS);

		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->confused", notice observable changes
 */
bool set_confused(int v)
{
	bool notice = 
		set_simple_field(
			&p_ptr->confused, v,
			TERM_WHITE, "You are confused!",
			TERM_WHITE, "You feel less confused now.");

	if (notice)
	{
		/* Redraw the "confused" */
		p_ptr->redraw |= (PR_FRAME);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->poisoned", notice observable changes
 */
bool set_poisoned(int v)
{
	bool notice = set_simple_field(
		&p_ptr->poisoned, v,
		TERM_WHITE, "You are poisoned!",
		TERM_WHITE, "You are no longer poisoned.");

	if (notice)
	{
		/* Redraw the "poisoned" */
		p_ptr->redraw |= (PR_FRAME);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->afraid", notice observable changes
 */
bool set_afraid(int v)
{
	bool notice = set_simple_field(
		&p_ptr->afraid, v,
		TERM_WHITE, "You are terrified!",
		TERM_WHITE, "You feel bolder now.");

	if (notice)
	{
		/* Redraw the "afraid" */
		p_ptr->redraw |= (PR_FRAME);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Mechanics for setting the "paralyzed" field.
 */
static bool set_paralyzed_aux(int v)
{
	bool notice;

	/* Normal processing */
	notice = set_simple_field(
		&p_ptr->paralyzed, v,
		TERM_WHITE, "You are paralyzed!",
		TERM_WHITE, "You can move again.");

	if (notice)
	{
		/* Redraw the state */
		p_ptr->redraw |= (PR_FRAME);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->paralyzed", notice observable changes
 */
bool set_paralyzed(int v)
{
	/* Paralysis effects do not accumulate -- this is to
	   prevent the uninteresting insta-death effect, but
	   still leave paralyzers highly dangerous if they're
	   faster than the player. */

	if (p_ptr->paralyzed > 0) {
		return false;
	}

	/* Normal processing */
	return set_paralyzed_aux(v);
}

/*
 * Decrement "p_ptr->paralyzed", notice observable changes
 */
void dec_paralyzed()
{
	set_paralyzed_aux(p_ptr->paralyzed - 1);
}


/*
 * Set "p_ptr->image", notice observable changes
 *
 * Note that we must redraw the map when hallucination changes.
 */
bool set_image(int v)
{
	bool notice = set_simple_field(
		&p_ptr->image, v,
		TERM_WHITE, "Oh, wow! Everything looks so cosmic now!",
		TERM_WHITE, "You can see clearly again.");

	if (notice)
	{
		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Update monsters */
		p_ptr->update |= (PU_MONSTERS);

		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD | PW_M_LIST);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->lightspeed", notice observable changes
 */
bool set_light_speed(int v)
{
	bool notice = 
		set_simple_field(
			&p_ptr->lightspeed, v,
			TERM_WHITE, "You feel as if time has stopped!",
			TERM_WHITE, "You feel time returning to its normal rate.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

bool set_fast(int v, int p)
{
	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!p_ptr->fast)
		{
			msg_print("You feel yourself moving faster!");
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (p_ptr->fast)
		{
			msg_print("You feel yourself slow down.");
			p = 0;
			notice = true;
		}
	}

	/* Use the value */
	p_ptr->fast = v;
	p_ptr->speed_factor = p;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}


/*
 * Set "p_ptr->slow", notice observable changes
 */
bool set_slow(int v)
{
	bool notice = set_simple_field(
		&p_ptr->slow, v,
		TERM_WHITE, "You feel yourself moving slower!",
		TERM_WHITE, "You feel yourself speed up.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->shield", notice observable changes
 */
bool set_shield(int v, int p, s16b o, s16b d1, s16b d2)
{
	bool notice = set_simple_field(
		&p_ptr->shield, v,
		TERM_WHITE, "A mystic shield forms around your body!",
		TERM_WHITE, "Your mystic shield crumbles away.");

	/* Use the values */
	p_ptr->shield_power = p;
	p_ptr->shield_opt = o;
	p_ptr->shield_power_opt = d1;
	p_ptr->shield_power_opt2 = d2;

	/* Notice? */
	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}



/*
 * Set "p_ptr->blessed", notice observable changes
 */
bool set_blessed(int v)
{
	bool notice = set_simple_field(
		&p_ptr->blessed, v,
		TERM_WHITE, "You feel righteous!",
		TERM_WHITE, "The prayer has expired.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->hero", notice observable changes
 */
bool set_hero(int v)
{
	bool notice = set_simple_field(
		&p_ptr->hero, v,
		TERM_WHITE, "You feel like a hero!",
		TERM_WHITE, "The heroism wears off.");

	if (notice)
	{
		/* Recalculate hitpoints */
		p_ptr->update |= (PU_HP);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->holy", notice observable changes
 */
bool set_holy(int v)
{
	bool notice = set_simple_field(
		&p_ptr->holy, v,
		TERM_WHITE, "You feel a holy aura around you!",
		TERM_WHITE, "The holy aura vanishes.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->shero", notice observable changes
 */
bool set_shero(int v)
{
	bool notice = set_simple_field(
		&p_ptr->shero, v,
		TERM_WHITE, "You feel like a killing machine!",
		TERM_WHITE, "You feel less berserk.");

	if (notice)
	{
		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Update monsters */
		p_ptr->update |= (PU_MONSTERS | PU_HP);

		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->protevil", notice observable changes
 */
bool set_protevil(int v)
{
	bool notice = set_simple_field(
		&p_ptr->protevil, v,
		TERM_WHITE, "You feel safe from evil!",
		TERM_WHITE, "You no longer feel safe from evil.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->set_shadow", notice observable changes
 */
bool set_shadow(int v)
{
	bool notice = set_simple_field(
		&p_ptr->tim_wraith, v,
		TERM_WHITE, "You leave the physical world and turn into a wraith-being!",
		TERM_WHITE, "You feel opaque.");

	if (notice)
	{
		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Update monsters */
		p_ptr->update |= (PU_MONSTERS);

		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}




/*
 * Set "p_ptr->invuln", notice observable changes
 */
bool set_invuln(int v)
{
	bool notice = set_simple_field(
		&p_ptr->invuln, v,
		TERM_L_BLUE, "Invulnerability!",
		TERM_L_RED, "The invulnerability wears off.");

	if (notice)
	{
		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);
		
		/* Update monsters */
		p_ptr->update |= (PU_MONSTERS);
		
		/* Window stuff */
		p_ptr->window |= (PW_OVERHEAD);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}



/*
 * Set "p_ptr->tim_esp", notice observable changes
 */
bool set_tim_esp(int v)
{
	bool notice = set_simple_field(
		&p_ptr->tim_esp, v,
		TERM_WHITE, "You feel your consciousness expand!",
		TERM_WHITE, "Your consciousness contracts again.");

	if (notice)
	{
		/* Update the monsters */
		p_ptr->update |= (PU_MONSTERS);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}

/*
 * Set "p_ptr->tim_thunder", notice observable changes
 */
bool set_tim_thunder(int v, int p1, int p2)
{
	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!p_ptr->tim_thunder)
		{
			msg_print("The air around you charges with lightning!");
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (p_ptr->tim_thunder)
		{
			msg_print("The air around you discharges.");
			notice = true;
			p1 = p2 = 0;
		}
	}

	/* Use the value */
	p_ptr->tim_thunder = v;
	p_ptr->tim_thunder_p1 = p1;
	p_ptr->tim_thunder_p2 = p2;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Update the monsters */
	p_ptr->update |= (PU_MONSTERS);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}

/*
 * Set "p_ptr->tim_invis", notice observable changes
 */
bool set_tim_invis(int v)
{
	bool notice = set_simple_field(
		&p_ptr->tim_invis, v,
		TERM_WHITE, "Your eyes feel very sensitive!",
		TERM_WHITE, "Your eyes feel less sensitive.");

	if (notice)
	{
		/* Update the monsters */
		p_ptr->update |= (PU_MONSTERS);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->tim_infra", notice observable changes
 */
bool set_tim_infra(int v)
{
	bool notice = set_simple_field(
		&p_ptr->tim_infra, v,
		TERM_WHITE, "Your eyes begin to tingle!",
		TERM_WHITE, "Your eyes stop tingling.");

	if (notice)
	{
		/* Update the monsters */
		p_ptr->update |= (PU_MONSTERS);

		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->oppose_acid", notice observable changes
 */
bool set_oppose_acid(int v)
{
	bool notice = set_simple_field(
		&p_ptr->oppose_acid, v,
		TERM_WHITE, "You feel resistant to acid!",
		TERM_WHITE, "You feel less resistant to acid.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->oppose_elec", notice observable changes
 */
bool set_oppose_elec(int v)
{
	bool notice = set_simple_field(
		&p_ptr->oppose_elec, v,
		TERM_WHITE, "You feel resistant to electricity!",
		TERM_WHITE, "You feel less resistant to electricity.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->oppose_fire", notice observable changes
 */
bool set_oppose_fire(int v)
{
	bool notice = set_simple_field(
		&p_ptr->oppose_fire, v,
		TERM_WHITE, "You feel resistant to fire!",
		TERM_WHITE, "You feel less resistant to fire.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->oppose_cold", notice observable changes
 */
bool set_oppose_cold(int v)
{
	bool notice = set_simple_field(
		&p_ptr->oppose_cold, v,
		TERM_WHITE, "You feel resistant to cold!",
		TERM_WHITE, "You feel less resistant to cold.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->oppose_pois", notice observable changes
 */
bool set_oppose_pois(int v)
{
	bool notice = set_simple_field(
		&p_ptr->oppose_pois, v,
		TERM_WHITE, "You feel resistant to poison!",
		TERM_WHITE, "You feel less resistant to poison.");

	if (notice)
	{
		/* Handle stuff */
		handle_stuff();
	}

	/* Result */
	return notice;
}


/*
 * Set "p_ptr->tim_regen", notice observable changes
 */
bool set_tim_regen(int v, int p)
{
	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	/* Open */
	if (v)
	{
		if (!p_ptr->tim_regen)
		{
			msg_print("Your body regenerates much more quickly!");
			notice = true;
		}
	}

	/* Shut */
	else
	{
		if (p_ptr->tim_regen)
		{
			p = 0;
			msg_print("Your body regenerates much more slowly.");
			notice = true;
		}
	}

	/* Use the value */
	p_ptr->tim_regen = v;
	p_ptr->tim_regen_pow = p;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}


/*
 * Set "p_ptr->stun", notice observable changes
 *
 * Note the special code to only notice "range" changes.
 */
bool set_stun(int v)
{
	int old_aux, new_aux;
	bool notice = false;


	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	if (race_flags_p(PR_NO_STUN)) v = 0;

	/* Knocked out */
	if (p_ptr->stun > 100)
	{
		old_aux = 3;
	}

	/* Heavy stun */
	else if (p_ptr->stun > 50)
	{
		old_aux = 2;
	}

	/* Stun */
	else if (p_ptr->stun > 0)
	{
		old_aux = 1;
	}

	/* None */
	else
	{
		old_aux = 0;
	}

	/* Knocked out */
	if (v > 100)
	{
		new_aux = 3;
	}

	/* Heavy stun */
	else if (v > 50)
	{
		new_aux = 2;
	}

	/* Stun */
	else if (v > 0)
	{
		new_aux = 1;
	}

	/* None */
	else
	{
		new_aux = 0;
	}

	/* Increase cut */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Stun */
		case 1:
			msg_print("You have been stunned.");
			break;

			/* Heavy stun */
		case 2:
			msg_print("You have been heavily stunned.");
			break;

			/* Knocked out */
		case 3:
			msg_print("You have been knocked out.");
			break;
		}

		if (randint(1000) < v || randint(16) == 1)
		{

			msg_print("A vicious blow hits your head.");
			if (randint(3) == 1)
			{
				if (!p_ptr->sustain_int)
				{
					do_dec_stat(A_INT, STAT_DEC_NORMAL);
				}
				if (!p_ptr->sustain_wis)
				{
					do_dec_stat(A_WIS, STAT_DEC_NORMAL);
				}
			}
			else if (randint(2) == 1)
			{
				if (!p_ptr->sustain_int)
				{
					do_dec_stat(A_INT, STAT_DEC_NORMAL);
				}
			}
			else
			{
				if (!p_ptr->sustain_wis)
				{
					do_dec_stat(A_WIS, STAT_DEC_NORMAL);
				}
			}
		}

		/* Notice */
		notice = true;
	}

	/* Decrease cut */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* None */
		case 0:
			msg_print("You are no longer stunned.");
			disturb_on_state();
			break;
		}

		/* Notice */
		notice = true;
	}

	/* Use the value */
	p_ptr->stun = v;

	/* No change */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Redraw the "stun" */
	p_ptr->redraw |= (PR_FRAME);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}


/*
 * Set "p_ptr->cut", notice observable changes
 *
 * Note the special code to only notice "range" changes.
 */
bool set_cut(int v)
{
	int old_aux, new_aux;

	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

	if (race_flags_p(PR_NO_CUT)) v = 0;

	/* Mortal wound */
	if (p_ptr->cut > 1000)
	{
		old_aux = 7;
	}

	/* Deep gash */
	else if (p_ptr->cut > 200)
	{
		old_aux = 6;
	}

	/* Severe cut */
	else if (p_ptr->cut > 100)
	{
		old_aux = 5;
	}

	/* Nasty cut */
	else if (p_ptr->cut > 50)
	{
		old_aux = 4;
	}

	/* Bad cut */
	else if (p_ptr->cut > 25)
	{
		old_aux = 3;
	}

	/* Light cut */
	else if (p_ptr->cut > 10)
	{
		old_aux = 2;
	}

	/* Graze */
	else if (p_ptr->cut > 0)
	{
		old_aux = 1;
	}

	/* None */
	else
	{
		old_aux = 0;
	}

	/* Mortal wound */
	if (v > 1000)
	{
		new_aux = 7;
	}

	/* Deep gash */
	else if (v > 200)
	{
		new_aux = 6;
	}

	/* Severe cut */
	else if (v > 100)
	{
		new_aux = 5;
	}

	/* Nasty cut */
	else if (v > 50)
	{
		new_aux = 4;
	}

	/* Bad cut */
	else if (v > 25)
	{
		new_aux = 3;
	}

	/* Light cut */
	else if (v > 10)
	{
		new_aux = 2;
	}

	/* Graze */
	else if (v > 0)
	{
		new_aux = 1;
	}

	/* None */
	else
	{
		new_aux = 0;
	}

	/* Increase cut */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Graze */
		case 1:
			msg_print("You have been given a graze.");
			break;

			/* Light cut */
		case 2:
			msg_print("You have been given a light cut.");
			break;

			/* Bad cut */
		case 3:
			msg_print("You have been given a bad cut.");
			break;

			/* Nasty cut */
		case 4:
			msg_print("You have been given a nasty cut.");
			break;

			/* Severe cut */
		case 5:
			msg_print("You have been given a severe cut.");
			break;

			/* Deep gash */
		case 6:
			msg_print("You have been given a deep gash.");
			break;

			/* Mortal wound */
		case 7:
			msg_print("You have been given a mortal wound.");
			break;
		}

		/* Notice */
		notice = true;

		if (randint(1000) < v || randint(16) == 1)
		{
			if (!p_ptr->sustain_chr)
			{
				msg_print("You have been horribly scarred.");

				do_dec_stat(A_CHR, STAT_DEC_NORMAL);
			}
		}
	}

	/* Decrease cut */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* None */
		case 0:
			msg_print("You are no longer bleeding.");
			disturb_on_state();
			break;
		}

		/* Notice */
		notice = true;
	}

	/* Use the value */
	p_ptr->cut = v;

	/* No change */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Redraw the "cut" */
	p_ptr->redraw |= (PR_FRAME);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}

void drop_from_wild()
{
	/* Hack -- Not if player were in normal mode in previous turn */
	if (!p_ptr->old_wild_mode) return;

	if (p_ptr->wild_mode && (!dun_level))
	{
		p_ptr->wilderness_x = p_ptr->px;
		p_ptr->wilderness_y = p_ptr->py;
		change_wild_mode();
		p_ptr->energy = 100;
		energy_use = 0;
	}
}

/*
 * Set "p_ptr->food", notice observable changes
 *
 * The "p_ptr->food" variable can get as large as 20000, allowing the
 * addition of the most "filling" item, Elvish Waybread, which adds
 * 7500 food units, without overflowing the 32767 maximum limit.
 *
 * Perhaps we should disturb the player with various messages,
 * especially messages about hunger status changes.  XXX XXX XXX
 *
 * Digestion of food is handled in "dungeon.c", in which, normally,
 * the player digests about 20 food units per 100 game turns, more
 * when "fast", more when "regenerating", less with "slow digestion",
 * but when the player is "gorged", he digests 100 food units per 10
 * game turns, or a full 1000 food units per 100 game turns.
 *
 * Note that the player's speed is reduced by 10 units while gorged,
 * so if the player eats a single food ration (5000 food units) when
 * full (15000 food units), he will be gorged for (5000/100)*10 = 500
 * game turns, or 500/(100/5) = 25 player turns (if nothing else is
 * affecting the player speed).
 */
bool set_food(int v)
{
	int old_aux, new_aux;

	bool notice = false;

	/* Hack -- Force good values */
	v = (v > 20000) ? 20000 : (v < 0) ? 0 : v;

	/* Fainting / Starving */
	if (p_ptr->food < PY_FOOD_FAINT)
	{
		old_aux = 0;
	}

	/* Weak */
	else if (p_ptr->food < PY_FOOD_WEAK)
	{
		old_aux = 1;
	}

	/* Hungry */
	else if (p_ptr->food < PY_FOOD_ALERT)
	{
		old_aux = 2;
	}

	/* Normal */
	else if (p_ptr->food < PY_FOOD_FULL)
	{
		old_aux = 3;
	}

	/* Full */
	else if (p_ptr->food < PY_FOOD_MAX)
	{
		old_aux = 4;
	}

	/* Gorged */
	else
	{
		old_aux = 5;
	}

	/* Fainting / Starving */
	if (v < PY_FOOD_FAINT)
	{
		new_aux = 0;
	}

	/* Weak */
	else if (v < PY_FOOD_WEAK)
	{
		new_aux = 1;
	}

	/* Hungry */
	else if (v < PY_FOOD_ALERT)
	{
		new_aux = 2;
	}

	/* Normal */
	else if (v < PY_FOOD_FULL)
	{
		new_aux = 3;
	}

	/* Full */
	else if (v < PY_FOOD_MAX)
	{
		new_aux = 4;
	}

	/* Gorged */
	else
	{
		new_aux = 5;
	}

	/* Food increase */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Weak */
		case 1:
			msg_print("You are still weak.");
			break;

			/* Hungry */
		case 2:
			msg_print("You are still hungry.");
			break;

			/* Normal */
		case 3:
			msg_print("You are no longer hungry.");
			break;

			/* Full */
		case 4:
			msg_print("You are full!");
			break;

			/* Bloated */
		case 5:
			msg_print("You have gorged yourself!");
			break;
		}

		/* Change */
		notice = true;
	}

	/* Food decrease */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Fainting / Starving */
		case 0:
			msg_print("You are getting faint from hunger!");
			drop_from_wild();
			break;

			/* Weak */
		case 1:
			msg_print("You are getting weak from hunger!");
			drop_from_wild();
			break;

			/* Hungry */
		case 2:
			msg_print("You are getting hungry.");
			break;

			/* Normal */
		case 3:
			msg_print("You are no longer full.");
			break;

			/* Full */
		case 4:
			msg_print("You are no longer gorged.");
			break;
		}

		/* Change */
		notice = true;
	}

	/* Use the value */
	p_ptr->food = v;

	/* Nothing to notice */
	if (!notice) return false;

	/* Disturb */
	disturb_on_state();

	/* Recalculate bonuses */
	p_ptr->update |= (PU_BONUS);

	/* Redraw hunger */
	p_ptr->redraw |= (PR_FRAME);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return true;
}


/*
 * Advance experience levels and print experience
 */
void check_experience()
{
	int gained = 0;
	bool level_corruption = false;


	/* Hack -- lower limit */
	if (p_ptr->exp < 0) p_ptr->exp = 0;

	/* Hack -- lower limit */
	if (p_ptr->max_exp < 0) p_ptr->max_exp = 0;

	/* Hack -- upper limit */
	if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP;

	/* Hack -- upper limit */
	if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP;

	/* Hack -- maintain "max" experience */
	if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp;

	/* Redraw experience */
	p_ptr->redraw |= (PR_FRAME);

	/* Handle stuff */
	handle_stuff();


	/* Lose levels while possible */
	while ((p_ptr->lev > 1) &&
	                (p_ptr->exp < (player_exp[p_ptr->lev - 2] * p_ptr->expfact / 100L)))
	{
		/* Lose a level */
		p_ptr->lev--;
		gained--;
		lite_spot(p_ptr->py, p_ptr->px);

		/* Update some stuff */
		p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);

		/* Redraw some stuff */
		p_ptr->redraw |= (PR_FRAME);

		/* Window stuff */
		p_ptr->window |= (PW_PLAYER);

		/* Handle stuff */
		handle_stuff();
	}


	/* Gain levels while possible */
	while ((p_ptr->lev < PY_MAX_LEVEL) &&
	        (p_ptr->exp >= (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L)))
	{
		/* Gain a level */
		p_ptr->lev++;
		gained++;
		lite_spot(p_ptr->py, p_ptr->px);

		/* Save the highest level */
		if (p_ptr->lev > p_ptr->max_plv)
		{
			p_ptr->max_plv = p_ptr->lev;
			if ((race_flags_p(PR_CORRUPT)) &&
			                (randint(3) == 1))
			{
				level_corruption = true;
			}
		}

		/* Message */
		cmsg_format(TERM_L_GREEN, "Welcome to level %d.", p_ptr->lev);

		if (p_ptr->skill_last_level < p_ptr->lev)
		{
			p_ptr->skill_last_level = p_ptr->lev;
			p_ptr->skill_points += modules[game_module_idx].skills.skill_per_level;
			cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
			p_ptr->redraw |= PR_FRAME;
		}

		/* Gain this level's abilities */
		apply_level_abilities(p_ptr->lev);

		/* Note level gain */
		{
			char note[80];

			/* Write note */
			sprintf(note, "Reached level %d", p_ptr->lev);
			add_note(note, 'L');
		}

		/* Update some stuff */
		p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_SANITY);

		/* Redraw some stuff */
		p_ptr->redraw |= (PR_FRAME);

		/* Window stuff */
		p_ptr->window |= (PW_PLAYER);

		/* Handle stuff */
		handle_stuff();

		if (level_corruption)
		{
			msg_print("You feel different...");
			corrupt_corrupted();
			level_corruption = false;
		}
	}

	/* Hook it! */
	{
		hook_player_level_in in = { gained };
		process_hooks_new(HOOK_PLAYER_LEVEL, &in, NULL);
	}
}

/*
 * Advance experience levels and print experience
 */
void check_experience_obj(object_type *o_ptr)
{
	/* Hack -- lower limit */
	if (o_ptr->exp < 0) o_ptr->exp = 0;

	/* Hack -- upper limit */
	if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;

	/* Gain levels while possible */
	while ((o_ptr->elevel < PY_MAX_LEVEL) &&
			(o_ptr->exp >= calc_object_need_exp(o_ptr)))
	{
		char buf[100];

		/* Add a level */
		o_ptr->elevel++;

		/* Get object name */
		object_desc(buf, o_ptr, 1, 0);
		cmsg_format(TERM_L_BLUE, "%s gains a level!", buf);

		/* What does it gains ? */
		object_gain_level(o_ptr);
	}
}


/*
 * Gain experience
 */
void gain_exp(s32b amount)
{
	if ((p_ptr->max_exp > 0) && (race_flags_p(PR_CORRUPT)))
	{
		if ((randint(p_ptr->max_exp) < amount) || (randint(12000000) < amount))
		{
			msg_print("You feel different...");
			corrupt_corrupted();
		};
		/* 12,000,000 is equal to double Morgoth's raw XP value (60,000 * his Dlev (100))*/
	};

	/* Gain some experience */
	p_ptr->exp += amount;

	/* Slowly recover from experience drainage */
	if (p_ptr->exp < p_ptr->max_exp)
	{
		/* Gain max experience (20%) (was 10%) */
		p_ptr->max_exp += amount / 5;
	}

	/* Check Experience */
	check_experience();
}


/*
 * Lose experience
 */
void lose_exp(s32b amount)
{
	/* Never drop below zero experience */
	if (amount > p_ptr->exp) amount = p_ptr->exp;

	/* Lose some experience */
	p_ptr->exp -= amount;

	/* Check Experience */
	check_experience();
}




/*
 * Hack -- Return the "automatic coin type" of a monster race
 * Used to allocate proper treasure when "Creeping coins" die
 *
 * XXX XXX XXX Note the use of actual "monster names"
 */
int get_coin_type(std::shared_ptr<monster_race const> r_ptr)
{
	const char *name = r_ptr->name;

	/* Analyze "coin" monsters */
	if (r_ptr->d_char == '$')
	{
		/* Look for textual clues */
		if (strstr(name, " copper ")) return (2);
		if (strstr(name, " silver ")) return (5);
		if (strstr(name, " gold ")) return (10);
		if (strstr(name, " mithril ")) return (16);
		if (strstr(name, " adamantite ")) return (17);

		/* Look for textual clues */
		if (strstr(name, "Copper ")) return (2);
		if (strstr(name, "Silver ")) return (5);
		if (strstr(name, "Gold ")) return (10);
		if (strstr(name, "Mithril ")) return (16);
		if (strstr(name, "Adamantite ")) return (17);
	}

	/* Assume nothing */
	return (0);
}

/*
 * This routine handles the production of corpses/skeletons/heads/skulls
 * when a monster is killed.
 */
void place_corpse(monster_type *m_ptr)
{
	const int x = m_ptr->fx;
	const int y = m_ptr->fy;

	/* Get local object */
	object_type object_type_body;
	object_type *i_ptr = &object_type_body;

	/* Get the race information */
	auto const r_ptr = m_ptr->race();

	/* It has a physical form */
	if (r_ptr->flags & RF_DROP_CORPSE)
	{
		/* Wipe the object */
		object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));

		/* Unique corpses are unique */
		if (r_ptr->flags & RF_UNIQUE)
		{
			object_aware(i_ptr);
			i_ptr->name1 = 201;
		}

		/* Calculate length of time before decay */
		i_ptr->pval = r_ptr->weight + rand_int(r_ptr->weight);

		/* Set weight */
		i_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;

		/* Remember what we are */
		i_ptr->pval2 = m_ptr->r_idx;

		/* Some hp */
		i_ptr->pval3 = ((maxroll(r_ptr->hdice, r_ptr->hside) + p_ptr->mhp) / 2);
		i_ptr->pval3 -= randint(i_ptr->pval3) / 3;

		i_ptr->found = OBJ_FOUND_MONSTER;
		i_ptr->found_aux1 = m_ptr->r_idx;
		i_ptr->found_aux2 = m_ptr->ego;
		i_ptr->found_aux3 = dungeon_type;
		i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

		/* Drop it in the dungeon */
		drop_near(i_ptr, -1, y, x);
	}

	/* The creature is an animated skeleton. */
	if (!(r_ptr->flags & RF_DROP_CORPSE) && (r_ptr->flags & RF_DROP_SKELETON))
	{
		/* Wipe the object */
		object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));

		/* Unique corpses are unique */
		if (r_ptr->flags & RF_UNIQUE)
		{
			object_aware(i_ptr);
			i_ptr->name1 = 201;
		}

		i_ptr->pval = 0;

		/* Set weight */
		i_ptr->weight = (r_ptr->weight / 4 + rand_int(r_ptr->weight) / 40) + 1;

		/* Remember what we are */
		i_ptr->pval2 = m_ptr->r_idx;

		i_ptr->found = OBJ_FOUND_MONSTER;
		i_ptr->found_aux1 = m_ptr->r_idx;
		i_ptr->found_aux2 = m_ptr->ego;
		i_ptr->found_aux3 = dungeon_type;
		i_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

		/* Drop it in the dungeon */
		drop_near(i_ptr, -1, y, x);
	}

}


/*
 * Check if monster race is in a given list. The list
 * must be NULL-terminated.
 */
static bool monster_race_in_list_p(monster_type *m_ptr, const char *races[])
{
	int i=0;
	for (i=0; races[i] != NULL; i++)
	{
		if (m_ptr->r_idx == test_monster_name(races[i])) {
			return true;
		}
	}
	/* Not found */
	return false;
}

/*
 * Handle the "death" of a monster (Gods)
 */
static void monster_death_gods(monster_type *m_ptr)
{
	if (p_ptr->pgod == GOD_AULE)
	{
		/* TODO: This should really be a racial flag
		   which can be added to the r_info file. */
		const char *DWARVES[] = {
			"Petty-dwarf",
			"Petty-dwarf mage",
			"Dark dwarven warrior",
			"Dark dwarven smith",
			"Dark dwarven lord",
			"Dark dwarven priest",
			"Dwarven warrior",
			NULL,
		};
		const char *UNIQUE_DWARVES[] = {
			"Nar, the Dwarf",
			"Naugladur, Lord of Nogrod",
			"Telchar the Smith",
			"Fundin Bluecloak",
			"Khim, Son of Mim",
			"Ibun, Son of Mim",
			"Mim, Betrayer of Turin",
			NULL,
		};

		/* Aule dislikes the killing of dwarves */
		if (monster_race_in_list_p(m_ptr, DWARVES))
		{
			inc_piety(GOD_ALL, -20);
		}

		/* ... and UNIQUE dwarves */
		if (monster_race_in_list_p(m_ptr, UNIQUE_DWARVES))
		{
			inc_piety(GOD_ALL, -500);
		}
	}

	if (p_ptr->pgod == GOD_ULMO)
	{
		/* He doesn't like it if you kill these monsters */
		const char *MINOR_RACES[] = {
			"Swordfish",
			"Barracuda",
			"Globefish",
			"Aquatic bear",
			"Pike",
			"Electric eel",
			"Giant crayfish",
			"Mermaid",
			"Leviathan",
			"Box jellyfish",
			"Giant piranha",
			"Piranha",
			"Ocean naga",
			"Whale",
			"Octopus",
			"Giant octopus",
			"Drowned soul",
			"Tiger shark",
			"Hammerhead shark",
			"Great white shark",
			"White shark",
			"Stargazer",
			"Flounder",
			"Giant turtle",
			"Killer whale",
			"Water naga",
			"Behemoth",
			NULL,
		};
		/* These monsters earn higher penalties */
		const char *MAJOR_RACES[] = {
			"Seahorse",
			"Aquatic elven warrior",
			"Aquatic elven mage",
			"Wavelord",
			"The Watcher in the Water",
			NULL,
		};

		if (monster_race_in_list_p(m_ptr, MINOR_RACES))
		{
			inc_piety(GOD_ALL, -20);
		}

		if (monster_race_in_list_p(m_ptr, MAJOR_RACES))
		{
			inc_piety(GOD_ALL, -500);
		}
	}

	if (p_ptr->pgod == GOD_MANDOS)
	{
		const char *MINOR_BONUS_RACES[] = {
			"Vampire",
			"Master vampire",
			"Oriental vampire",
			"Vampire lord",
			"Vampire orc",
			"Vampire yeek",
			"Vampire ogre",
			"Vampire troll",
			"Vampire dwarf",
			"Vampire gnome",
			"Elder vampire",
			NULL,
		};
		const char *MAJOR_BONUS_RACES[] = {
			"Vampire elf",
			"Thuringwethil, the Vampire Messenger",
			NULL,
		};
		const char *MINOR_PENALTY[] = {
			"Dark elf",
			"Dark elven druid",
			"Eol, the Dark Elf",
			"Maeglin, the Traitor of Gondolin",
			"Dark elven mage",
			"Dark elven warrior",
			"Dark elven priest",
			"Dark elven lord",
			"Dark elven warlock",
			"Dark elven sorcerer",
			NULL,
		};
		const char *MEDIUM_PENALTY[] = {
			"Glorfindel of Rivendell",
			"Finrod Felagund",
			"Thranduil, King of the Wood Elves",
			"Aquatic elven warrior",
			"Aquatic elven mage",
			"High-elven ranger",
			"Elven archer",
			NULL,
		};
		const char *MAJOR_PENALTY[] = {
			"Child spirit",
			"Young spirit",
			"Mature spirit",
			"Experienced spirit",
			"Wise spirit",
			NULL,
		};

		if (monster_race_in_list_p(m_ptr, MINOR_BONUS_RACES))
		{
			/* He really likes it if you kill Vampires
			 * (but not the adventurer kind :P) */
			inc_piety(GOD_ALL, 50);
		}

		if (monster_race_in_list_p(m_ptr, MAJOR_BONUS_RACES))
		{
			/* He *loves* it if you kill vampire Elves. He
			 * will also thank you extra kindly if you
			 * kill Thuringwethil */
			inc_piety(GOD_ALL, 200);
		}

		if (monster_race_in_list_p(m_ptr, MINOR_PENALTY))
		{
			/* He doesn't like it if you kill normal Elves
			 * (means more work for him :P) */
			inc_piety(GOD_ALL, -20);
		}

		if (monster_race_in_list_p(m_ptr, MEDIUM_PENALTY))
		{
			/* He hates it if you kill coaligned Elves */
			inc_piety(GOD_ALL, -200);
		}

		if (monster_race_in_list_p(m_ptr, MAJOR_PENALTY))
		{
			/* He *hates* it if you kill the coaligned Spirits */
			inc_piety(GOD_ALL, -1000);
		}
	}
}

/*
 * Handle the "death" of a monster.
 *
 * Disperse treasures centered at the monster location based on the
 * various flags contained in the monster flags fields.
 *
 * Check for "Quest" completion when a quest monster is killed.
 *
 * Note that only the player can induce "monster_death()" on Uniques.
 * Thus (for now) all Quest monsters should be Uniques.
 *
 * Note that monsters can now carry objects, and when a monster dies,
 * it drops all of its objects, which may disappear in crowded rooms.
 */
void monster_death(int m_idx)
{
	auto const &d_info = game->edit_data.d_info;
	auto const &f_info = game->edit_data.f_info;
	auto &a_info = game->edit_data.a_info;

	monster_type *m_ptr = &m_list[m_idx];

	auto const r_ptr = m_ptr->race();

	bool create_stairs = false;
	int force_coin = get_coin_type(r_ptr);

	object_type forge;
	object_type *q_ptr;

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

	/* Process the appropriate hooks */
	{
		struct hook_monster_death_in in = { m_idx };
		process_hooks_new(HOOK_MONSTER_DEATH, &in, NULL);
	}

	/* Per-god processing */
	monster_death_gods(m_ptr);

	/* If companion dies, take note */
	if (m_ptr->status == MSTATUS_COMPANION) p_ptr->companion_killed++;

	/* Handle reviving if undead */
	if ((p_ptr->necro_extra & CLASS_UNDEAD) && p_ptr->necro_extra2)
	{
		p_ptr->necro_extra2--;

		if (!p_ptr->necro_extra2)
		{
			msg_print("Your death has been avenged -- you return to life.");
			p_ptr->necro_extra &= ~CLASS_UNDEAD;

			/* Display the hitpoints */
			p_ptr->update |= (PU_HP);
			p_ptr->redraw |= (PR_FRAME);

			/* Window stuff */
			p_ptr->window |= (PW_PLAYER);
		}
		else
		{
			msg_format("You still have to kill %d monster%s.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
		}
	}

	/* If the doppleganger die, the variable must be set accordingly */
	if (r_ptr->flags & RF_DOPPLEGANGER) doppleganger = 0;

	/* Need copy of object list since we're going to mutate it */
	auto const object_idxs(m_ptr->hold_o_idxs);

	/* Drop objects being carried */
	for (auto const this_o_idx: object_idxs)
	{
		/* Acquire object */
		object_type * o_ptr = &o_list[this_o_idx];

		/* Paranoia */
		o_ptr->held_m_idx = 0;

		/* Get local object */
		q_ptr = &forge;

		/* Copy the object */
		object_copy(q_ptr, o_ptr);

		/* Delete the object */
		delete_object_idx(this_o_idx);

		/* Drop it */
		drop_near(q_ptr, -1, y, x);
	}

	/* Forget objects */
	m_ptr->hold_o_idxs.clear();

	/* Average dungeon and monster levels */
	object_level = (dun_level + m_ptr->level) / 2;

	/* Mega^2-hack -- destroying the Stormbringer gives it us! */
	if (strstr(r_ptr->name, "Stormbringer"))
	{
		/* Get local object */
		q_ptr = &forge;

		/* Prepare to make the Stormbringer */
		object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BLADE_OF_CHAOS));

		/* Mega-Hack -- Name the sword  */

		q_ptr->artifact_name = "'Stormbringer'";
		q_ptr->to_h = 16;
		q_ptr->to_d = 16;
		q_ptr->ds = 6;
		q_ptr->dd = 6;
		q_ptr->pval = 2;

		q_ptr->art_flags |=
			TR_VAMPIRIC |
			TR_STR |
			TR_CON |
			TR_BLOWS |
			TR_FREE_ACT |
			TR_HOLD_LIFE |
			TR_RES_NEXUS |
			TR_RES_CHAOS |
			TR_RES_NETHER |
			TR_RES_CONF |
			TR_IGNORE_ACID |
			TR_IGNORE_ELEC |
			TR_IGNORE_FIRE |
			TR_IGNORE_COLD |
			TR_NO_TELE |
			TR_CURSED |
			TR_HEAVY_CURSE;

		if (randint(2) == 1)
		{
			q_ptr->art_flags |= TR_DRAIN_EXP;
		}
		else
		{
			q_ptr->art_flags |= TR_AGGRAVATE;
		}

		q_ptr->found = OBJ_FOUND_MONSTER;
		q_ptr->found_aux1 = m_ptr->r_idx;
		q_ptr->found_aux2 = m_ptr->ego;
		q_ptr->found_aux3 = dungeon_type;
		q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

		/* Drop it in the dungeon */
		drop_near(q_ptr, -1, y, x);
	}

	/*
	 * Mega^3-hack: killing a 'Warrior of the Dawn' is likely to
	 * spawn another in the fallen one's place!
	 */
	else if (strstr(r_ptr->name, "the Dawn"))
	{
		if (!(randint(20) == 13))
		{
			int wy = p_ptr->py, wx = p_ptr->px;
			int attempts = 100;

			do
			{
				scatter(&wy, &wx, p_ptr->py, p_ptr->px, 20);
			}
			while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);

			if (attempts > 0)
			{
				if (is_friend(m_ptr) > 0)
				{
					if (summon_specific_friendly(wy, wx, 100, SUMMON_DAWN, false))
					{
						if (player_can_see_bold(wy, wx))
							msg_print ("A new warrior steps forth!");
					}
				}
				else
				{
					if (summon_specific(wy, wx, 100, SUMMON_DAWN))
					{
						if (player_can_see_bold(wy, wx))
							msg_print ("A new warrior steps forth!");
					}
				}
			}
		}
	}

	/* One more ultra-hack: An Unmaker goes out with a big bang! */
	else if (strstr(r_ptr->name, "Unmaker"))
	{
		int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
		project(m_idx, 6, y, x, 100, GF_CHAOS, flg);
	}
	/* Pink horrors are replaced with 2 Blue horrors */
	else if (strstr(r_ptr->name, "ink horror"))
	{
		for (int i = 0; i < 2; i++)
		{
			int wy = p_ptr->py, wx = p_ptr->px;
			int attempts = 100;

			do
			{
				scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3);
			}
			while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);

			if (attempts > 0)
			{
				if (summon_specific(wy, wx, 100, SUMMON_BLUE_HORROR))
				{
					if (player_can_see_bold(wy, wx))
						msg_print ("A blue horror appears!");
				}
			}
		}
	}

	/* Mega-Hack -- drop "winner" treasures */
	else if (r_ptr->flags & RF_DROP_CHOSEN)
	{
		if (strstr(r_ptr->name, "Morgoth, Lord of Darkness"))
		{
			/* Get local object */
			q_ptr = &forge;

			/* Mega-Hack -- Prepare to make "Grond" */
			object_prep(q_ptr, lookup_kind(TV_HAFTED, SV_GROND));

			/* Mega-Hack -- Mark this item as "Grond" */
			q_ptr->name1 = ART_GROND;

			/* Mega-Hack -- Actually create "Grond" */
			apply_magic(q_ptr, -1, true, true, true);

			/* Drop it in the dungeon */
			drop_near(q_ptr, -1, y, x);

			/* Get local object */
			q_ptr = &forge;

			/* Mega-Hack -- Prepare to make "Morgoth" */
			object_prep(q_ptr, lookup_kind(TV_CROWN, SV_MORGOTH));

			/* Mega-Hack -- Mark this item as "Morgoth" */
			q_ptr->name1 = ART_MORGOTH;

			/* Mega-Hack -- Actually create "Morgoth" */
			apply_magic(q_ptr, -1, true, true, true);

			q_ptr->found = OBJ_FOUND_MONSTER;
			q_ptr->found_aux1 = m_ptr->r_idx;
			q_ptr->found_aux2 = m_ptr->ego;
			q_ptr->found_aux3 = dungeon_type;
			q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

			/* Drop it in the dungeon */
			drop_near(q_ptr, -1, y, x);
		}
		else if (strstr(r_ptr->name, "Smeagol"))
		{
			/* Get local object */
			q_ptr = &forge;

			object_wipe(q_ptr);

			/* Mega-Hack -- Prepare to make a ring of invisibility */
			object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
			q_ptr->number = 1;

			apply_magic(q_ptr, -1, true, true, false);

			q_ptr->found = OBJ_FOUND_MONSTER;
			q_ptr->found_aux1 = m_ptr->r_idx;
			q_ptr->found_aux2 = m_ptr->ego;
			q_ptr->found_aux3 = dungeon_type;
			q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

			/* Drop it in the dungeon */
			drop_near(q_ptr, -1, y, x);
		}
		else if (r_ptr->flags & RF_NAZGUL)
		{
			/* Get local object */
			q_ptr = &forge;

			object_wipe(q_ptr);

			/* Mega-Hack -- Prepare to make a Ring of Power */
			object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_SPECIAL));
			q_ptr->number = 1;

			apply_magic(q_ptr, -1, true, true, false);

			/* Create a random artifact */
			create_artifact(q_ptr, true, false);

			/* Save the inscription */
			q_ptr->artifact_name = fmt::format("of {}", r_ptr->name);

			q_ptr->found = OBJ_FOUND_MONSTER;
			q_ptr->found_aux1 = m_ptr->r_idx;
			q_ptr->found_aux2 = m_ptr->ego;
			q_ptr->found_aux3 = dungeon_type;
			q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

			/* Drop it in the dungeon */
			drop_near(q_ptr, -1, y, x);
		}
		else
		{
			byte a_idx = r_ptr->artifact_idx;
			int chance = r_ptr->artifact_chance;

			if ((a_idx > 0) && ((randint(99) < chance) || (wizard)))
			{
				if (a_info[a_idx].cur_num == 0)
				{
					auto a_ptr = &a_info[a_idx];

					/* Get local object */
					q_ptr = &forge;

					/* Wipe the object */
					object_wipe(q_ptr);

					/* Acquire the "kind" index */
					int I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);

					/* Create the artifact */
					object_prep(q_ptr, I_kind);

					/* Save the name */
					q_ptr->name1 = a_idx;

					/* Extract the fields */
					q_ptr->pval = a_ptr->pval;
					q_ptr->ac = a_ptr->ac;
					q_ptr->dd = a_ptr->dd;
					q_ptr->ds = a_ptr->ds;
					q_ptr->to_a = a_ptr->to_a;
					q_ptr->to_h = a_ptr->to_h;
					q_ptr->to_d = a_ptr->to_d;
					q_ptr->weight = a_ptr->weight;

					/* Hack -- acquire "cursed" flag */
					if (a_ptr->flags & TR_CURSED)
					{
						q_ptr->art_flags |= TR_CURSED;
					}

					random_artifact_resistance(q_ptr);

					a_info[a_idx].cur_num = 1;

					q_ptr->found = OBJ_FOUND_MONSTER;
					q_ptr->found_aux1 = m_ptr->r_idx;
					q_ptr->found_aux2 = m_ptr->ego;
					q_ptr->found_aux3 = dungeon_type;
					q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);

					/* Drop the artifact from heaven */
					drop_near(q_ptr, -1, y, x);
				}
			}
		}
	}

	/* Hack - the protected monsters must be advanged */
	else if (r_ptr->flags & RF_WYRM_PROTECT)
	{
		int xx = x, yy = y;
		int attempts = 100;

		cmsg_print(TERM_VIOLET, "This monster was under the protection of a Great Wyrm of Power!");

		do
		{
			scatter(&yy, &xx, y, x, 6);
		}
		while (!(in_bounds(yy, xx) && cave_floor_bold(yy, xx)) && --attempts);

		place_monster_aux(yy, xx, test_monster_name("Great Wyrm of Power"), false, false, m_ptr->status);
	}

	/* Let monsters explode! */
	for (int i = 0; i < 4; i++)
	{
		if (m_ptr->blow[i].method == RBM_EXPLODE)
		{
			int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
			int typ = GF_MISSILE;
			int d_dice = m_ptr->blow[i].d_dice;
			int d_side = m_ptr->blow[i].d_side;
			int damage = damroll(d_dice, d_side);

			switch (m_ptr->blow[i].effect)
			{
			case RBE_HURT:
				typ = GF_MISSILE;
				break;
			case RBE_POISON:
				typ = GF_POIS;
				break;
			case RBE_UN_BONUS:
				typ = GF_DISENCHANT;
				break;
			case RBE_UN_POWER:
				typ = GF_MISSILE;
				break;  /* ToDo: Apply the correct effects */
			case RBE_EAT_GOLD:
				typ = GF_MISSILE;
				break;
			case RBE_EAT_ITEM:
				typ = GF_MISSILE;
				break;
			case RBE_EAT_FOOD:
				typ = GF_MISSILE;
				break;
			case RBE_EAT_LITE:
				typ = GF_MISSILE;
				break;
			case RBE_ACID:
				typ = GF_ACID;
				break;
			case RBE_ELEC:
				typ = GF_ELEC;
				break;
			case RBE_FIRE:
				typ = GF_FIRE;
				break;
			case RBE_COLD:
				typ = GF_COLD;
				break;
			case RBE_BLIND:
				typ = GF_MISSILE;
				break;
			case RBE_HALLU:
				typ = GF_CONFUSION;
				break;
			case RBE_CONFUSE:
				typ = GF_CONFUSION;
				break;
			case RBE_TERRIFY:
				typ = GF_MISSILE;
				break;
			case RBE_PARALYZE:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_STR:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_DEX:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_CON:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_INT:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_WIS:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_CHR:
				typ = GF_MISSILE;
				break;
			case RBE_LOSE_ALL:
				typ = GF_MISSILE;
				break;
			case RBE_PARASITE:
				typ = GF_MISSILE;
				break;
			case RBE_SHATTER:
				typ = GF_ROCKET;
				break;
			case RBE_EXP_10:
				typ = GF_MISSILE;
				break;
			case RBE_EXP_20:
				typ = GF_MISSILE;
				break;
			case RBE_EXP_40:
				typ = GF_MISSILE;
				break;
			case RBE_EXP_80:
				typ = GF_MISSILE;
				break;
			case RBE_DISEASE:
				typ = GF_POIS;
				break;
			case RBE_TIME:
				typ = GF_TIME;
				break;
			case RBE_SANITY:
				typ = GF_MISSILE;
				break;
			}

			project(m_idx, 3, y, x, damage, typ, flg);
			break;
		}
	}

	if ((!force_coin) && (magik(10 + get_skill_scale(SKILL_PRESERVATION, 75))) && (!(m_ptr->mflag & MFLAG_NO_DROP)))
		place_corpse(m_ptr);

	/* Create a magical staircase */
	if (create_stairs && (dun_level < d_info[dungeon_type].maxdepth))
	{
		for (int i = -1; i <= 1; i++)
		{
			for (int j = -1; j <= 1; j++)
			{
				if (!(f_info[cave[y + j][x + i].feat].flags & FF_PERMANENT))
				{
					cave_set_feat(y + j, x + i, d_info[dungeon_type].floor1);
				}
			}
		}

		/* Stagger around */
		while (!cave_valid_bold(y, x))
		{
			/* Pick a location */
			int ny, nx;
			scatter(&ny, &nx, y, x, 1);

			/* Stagger */
			y = ny;
			x = nx;
		}

		/* Destroy any objects */
		delete_object(y, x);

		/* Explain the staircase */
		msg_print("A magical staircase appears...");

		/* Create stairs down */
		cave_set_feat(y, x, FEAT_MORE);

		/* Remember to update everything */
		p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS);
	}
}




/*
 * Decreases monsters hit points, handling monster death.
 *
 * We return true if the monster has been killed (and deleted).
 *
 * We announce monster death (using an optional "death message"
 * if given, and a otherwise a generic killed/destroyed message).
 *
 * Only "physical attacks" can induce the "You have slain" message.
 * Missile and Spell attacks will induce the "dies" message, or
 * various "specialized" messages.  Note that "You have destroyed"
 * and "is destroyed" are synonyms for "You have slain" and "dies".
 *
 * Hack -- unseen monsters yield "You have killed it." message.
 *
 * Added fear (DGK) and check whether to print fear messages -CWS
 *
 * Genericized name, sex, and capitilization -BEN-
 *
 * Hack -- we "delay" fear messages by passing around a "fear" flag.
 *
 * XXX XXX XXX Consider decreasing monster experience over time, say,
 * by using "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))"
 * instead of simply "(m_exp * m_lev) / (p_lev)", to make the first
 * monster worth more than subsequent monsters.  This would also need
 * to induce changes in the monster recall code.
 */
bool mon_take_hit(int m_idx, int dam, bool *fear, const char *note)
{
	monster_type *m_ptr = &m_list[m_idx];
	auto const r_idx = m_ptr->r_idx;
	auto const r_ptr = m_ptr->race();

	/* Redraw (later) if needed */
	if (health_who == m_idx)
	{
		p_ptr->redraw |= (PR_FRAME);
	}

	/* Some monsters are immune to death */
	if (r_ptr->flags & RF_NO_DEATH)
	{
		return false;
	}

	/* Wake it up */
	m_ptr->csleep = 0;

	/* Hurt it */
	m_ptr->hp -= dam;

	/* It is dead now */
	if (m_ptr->hp < 0)
	{
		char m_name[80];

		/* Lets face it, you cannot get rid of a possessor that easily */
		if (m_ptr->possessor)
		{
			ai_deincarnate(m_idx);

			return false;
		}

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

		if ((r_ptr->flags & RF_DG_CURSE) && (randint(2) == 1))
		{
			int curses = 2 + randint(5);

			cmsg_format(TERM_VIOLET, "%^s puts a terrible Morgothian curse on you!", m_name);
			curse_equipment_dg(100, 50);

			do
			{
				activate_dg_curse();
			}
			while (--curses);
		}

		if (r_ptr->flags & RF_CAN_SPEAK)
		{
			char line_got[80];
			/* Dump a message */

			get_rnd_line("mondeath.txt", line_got);
			msg_format("%^s says: %s", m_name, line_got);
		}

		/* Death by Missile/Spell attack */
		if (note)
		{
			cmsg_format(TERM_L_RED, "%^s%s", m_name, note);
		}

		/* Death by physical attack -- invisible monster */
		else if (!m_ptr->ml)
		{
			cmsg_format(TERM_L_RED, "You have killed %s.", m_name);
		}

		/* Death by Physical attack -- non-living monster */
		else if ((r_ptr->flags & RF_DEMON) ||
		                (r_ptr->flags & RF_UNDEAD) ||
		                (r_ptr->flags & RF_STUPID) ||
		                (r_ptr->flags & RF_NONLIVING) ||
		                (strchr("Evg", r_ptr->d_char)))
		{
			cmsg_format(TERM_L_RED, "You have destroyed %s.", m_name);
		}

		/* Death by Physical attack -- living monster */
		else
		{
			cmsg_format(TERM_L_RED, "You have slain %s.", m_name);
		}

		/* Maximum player level */
		const s32b div = p_ptr->max_plv;

		if (m_ptr->status < MSTATUS_FRIEND)
		{
			/* Give some experience for the kill */
			int new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;

			/* Handle fractional experience */
			const s32b new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
			                * 0x10000L / div) + p_ptr->exp_frac;

			/* Keep track of experience */
			if (new_exp_frac >= 0x10000L)
			{
				new_exp++;
				p_ptr->exp_frac = new_exp_frac - 0x10000L;
			}
			else
			{
				p_ptr->exp_frac = new_exp_frac;
			}

			/* Gain experience */
			gain_exp(new_exp);
		}

		if (!note)
		{
			/* Access the weapon */
			object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
			auto const flags = object_flags(o_ptr);

			/* Can the weapon gain levels ? */
			if (o_ptr->k_ptr && (flags & TR_LEVELS))
			{
				/* Give some experience for the kill */
				const int new_exp = ((long)r_ptr->mexp * m_ptr->level) / (div * 2);

				/* Gain experience */
				o_ptr->exp += new_exp;
				check_experience_obj(o_ptr);
			}
		}

		/* When the player kills a Unique, it stays dead */
		if (r_ptr->flags & RF_UNIQUE)
		{
			r_ptr->max_num = 0;
		}

		/* Generate treasure */
		monster_death(m_idx);

		/* Eru doesn't appreciate good monster death */
		if (r_ptr->flags & RF_GOOD)
		{
			inc_piety(GOD_ERU, -7 * m_ptr->level);
			inc_piety(GOD_MANWE, -10 * m_ptr->level);
			inc_piety(GOD_MELKOR, 3 * m_ptr->level);
		}
		else
		{
			inc_piety(GOD_MELKOR, 1 + m_ptr->level / 2);
		}

		/* Manwe appreciate evil monster death */
		if (r_ptr->flags & RF_EVIL)
		{
			int inc = std::max(1, m_ptr->level / 2);

			if (praying_to(GOD_MANWE))
			{
				inc_piety(GOD_MANWE, inc);
			}

			inc = std::max(2, inc);

			inc_piety(GOD_TULKAS, inc / 2);

			if (praying_to(GOD_TULKAS))
			{
				inc_piety(GOD_TULKAS, inc / 2);
				if (r_ptr->flags & RF_DEMON)
				{
					inc_piety(GOD_TULKAS, inc);
				}
			}
		}

		/* Yavanna likes when corruption is destroyed */
		if ((r_ptr->flags & RF_NONLIVING) || (r_ptr->flags & RF_DEMON) || (r_ptr->flags & RF_UNDEAD))
		{
			int inc = std::max(1, m_ptr->level / 2);
			inc_piety(GOD_YAVANNA, inc);
		}

		/* Yavanna doesnt like any killing in her name */
		if (praying_to(GOD_YAVANNA))
		{
			int inc = std::max(1, m_ptr->level / 2);
			inc_piety(GOD_YAVANNA, -inc);

			/* Killing animals in her name is a VERY bad idea */
			if (r_ptr->flags & RF_ANIMAL)
				inc_piety(GOD_YAVANNA, -(inc * 3));
		}

		/* SHould we absorb its soul? */
		if (p_ptr->absorb_soul && (!(r_ptr->flags & RF_UNDEAD)) && (!(r_ptr->flags & RF_NONLIVING)))
		{
			msg_print("You absorb the life of the dying soul.");
			hp_player(1 + (m_ptr->level / 2) + get_skill_scale(SKILL_NECROMANCY, 40));
		}

		/*
		* XXX XXX XXX Mega-Hack -- Remove random quest rendered
		* impossible
		*/
		if (r_ptr->flags & RF_UNIQUE)
		{
			int i;

			/* Search for all the random quests */
			for (i = 0; i < MAX_RANDOM_QUEST; i++)
			{
				random_quest *q_ptr = &random_quests[i];

				/* Ignore invalid entries */
				if (q_ptr->type == 0) continue;

				/* It's done */
				if (q_ptr->done) continue;

				/*
				 * XXX XXX XXX Not yet completed quest is
				 * to kill a unique you've just killed
				 */
				if (r_idx == q_ptr->r_idx)
				{
					/* Invalidate it */
					q_ptr->type = 0;
				}
			}
		}

		/* Make note of unique kills */
		if (r_ptr->flags & RF_UNIQUE)
		{
			char note[80];

			/* Write note */
			sprintf(note, "Killed %s", r_ptr->name);

			add_note(note, 'U');
		}

		/* Recall even invisible uniques or winners */
		if (m_ptr->ml || (r_ptr->flags & RF_UNIQUE))
		{
			/* Count kills this life */
			if (r_ptr->r_pkills < MAX_SHORT) r_ptr->r_pkills++;

			/* Hack -- Auto-recall */
			monster_race_track(m_ptr->r_idx, m_ptr->ego);
		}

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

		/* Not afraid */
		if (fear != nullptr)
		{
			(*fear) = false;
		}

		/* Monster is dead */
		return true;
	}

	/* Apply fear */
	mon_handle_fear(m_ptr, dam, fear);

	/* Not dead yet */
	return false;
}


/*
 * Get term size and calculate screen size
 */
void get_screen_size(int *wid_p, int *hgt_p)
{
	Term_get_size(wid_p, hgt_p);
	*hgt_p -= ROW_MAP + 1;
	*wid_p -= COL_MAP + 1;
}

/*
 * Calculates current boundaries
 * Called below.
 */
static void panel_bounds()
{
	int wid, hgt;

	/* Retrieve current screen size */
	get_screen_size(&wid, &hgt);

	/* + 24 - 1 - 2 =  + 21 */
	panel_row_max = panel_row_min + hgt - 1;
	panel_row_prt = panel_row_min - ROW_MAP;

	/* Paranoia -- Boundary check */
	if (panel_row_max > cur_hgt - 1) panel_row_max = cur_hgt - 1;

	panel_col_max = panel_col_min + wid - 1;
	panel_col_prt = panel_col_min - COL_MAP;

	/* Paranoia -- Boundary check */
	if (panel_col_max > cur_wid - 1) panel_col_max = cur_wid - 1;
}


/*
 * Handle a request to change the current panel
 *
 * Return true if the panel was changed.
 *
 * Also used in do_cmd_locate()
 */
bool change_panel(int dy, int dx)
{
	int y, x;
	int wid, hgt;

	/* Get size */
	get_screen_size(&wid, &hgt);

	/* Apply the motion */
	y = panel_row_min + dy * (hgt / 2);
	x = panel_col_min + dx * (wid / 2);

	/* Calculate bounds */
	if (y > cur_hgt - hgt) y = cur_hgt - hgt;
	if (y < 0) y = 0;
	if (x > cur_wid - wid) x = cur_wid - wid;
	if (x < 0) x = 0;

	/* Handle changes */
	if ((y != panel_row_min) || (x != panel_col_min))
	{
		/* Save the new panel info */
		panel_row_min = y;
		panel_col_min = x;

		/* Recalculate the boundaries */
		panel_bounds();

		/* Update stuff */
		p_ptr->update |= (PU_MONSTERS);

		/* Redraw map */
		p_ptr->redraw |= (PR_MAP);

		/* Handle stuff */
		handle_stuff();

		/* Success */
		return true;
	}

	/* No changes */
	return false;
}


/*
 * Given an row (y) and col (x), this routine detects when a move
 * off the screen has occurred and figures new borders. -RAK-
 *
 * "Update" forces a "full update" to take place.
 *
 * The map is reprinted if necessary, and "true" is returned.
 */
void verify_panel()
{
	int y = p_ptr->py;
	int x = p_ptr->px;

	int wid, hgt;

	int prow_min;
	int pcol_min;

	int panel_hgt, panel_wid;

	int max_prow_min;
	int max_pcol_min;


	/*
	 * Make sure panel_row/col_max have correct values -- now taken care of
	 * by the hook function below, which eliminates glitches, but does it
	 * in a very hackish way XXX XXX XXX
	 */
	/* panel_bounds(); */

	/* Get size */
	get_screen_size(&wid, &hgt);

	/* Calculate scroll amount */
	panel_hgt = hgt / 2;
	panel_wid = wid / 2;

	/* Upper boundary of panel_row/col_min */
	max_prow_min = cur_hgt - hgt;
	max_pcol_min = cur_wid - wid;

	/* Boundary check */
	if (max_prow_min < 0) max_prow_min = 0;
	if (max_pcol_min < 0) max_pcol_min = 0;

	/* An option: center on player */
	if (options->center_player)
	{
		/* Center vertically */
		prow_min = y - panel_hgt;

		/* Boundary check */
		if (prow_min < 0) prow_min = 0;
		else if (prow_min > max_prow_min) prow_min = max_prow_min;

		/* Center horizontally */
		pcol_min = x - panel_wid;

		/* Boundary check */
		if (pcol_min < 0) pcol_min = 0;
		else if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
	}

	/* No centering */
	else
	{
		prow_min = panel_row_min;
		pcol_min = panel_col_min;

		/* Scroll screen when 2 grids from top/bottom edge */
		if (y > panel_row_max - 2)
		{
			while (y > prow_min + hgt - 2)
			{
				prow_min += panel_hgt;
			}

			if (prow_min > max_prow_min) prow_min = max_prow_min;
		}

		if (y < panel_row_min + 2)
		{
			while (y < prow_min + 2)
			{
				prow_min -= panel_hgt;
			}

			if (prow_min < 0) prow_min = 0;
		}

		/* Scroll screen when 4 grids from left/right edge */
		if (x > panel_col_max - 4)
		{
			while (x > pcol_min + wid - 4)
			{
				pcol_min += panel_wid;
			}

			if (pcol_min > max_pcol_min) pcol_min = max_pcol_min;
		}

		if (x < panel_col_min + 4)
		{
			while (x < pcol_min + 4)
			{
				pcol_min -= panel_wid;
			}

			if (pcol_min < 0) pcol_min = 0;
		}
	}

	/* Check for "no change" */
	if ((prow_min == panel_row_min) && (pcol_min == panel_col_min)) return;

	/* Save the new panel info */
	panel_row_min = prow_min;
	panel_col_min = pcol_min;

	/* Hack -- optional disturb on "panel change" */
	if (options->disturb_panel && !options->center_player)
	{
		disturb();
	}

	/* Recalculate the boundaries */
	panel_bounds();

	/* Update stuff */
	p_ptr->update |= (PU_MONSTERS);

	/* Redraw map */
	p_ptr->redraw |= (PR_MAP);

	/* Window stuff */
	p_ptr->window |= (PW_OVERHEAD);
}


/*
 * Map resizing whenever the main term changes size
 */
void resize_map()
{
	/* Only if the dungeon exists */
	if (!character_dungeon)
	{
		return;
	}

	/* Mega-Hack -- No panel yet, assume illegal panel */
	panel_row_min = cur_hgt;
	panel_row_max = 0;
	panel_col_min = cur_wid;
	panel_col_max = 0;

	/* Select player panel */
	verify_panel();

	/*
	 * The following should be the same as the main window code
	 * in the do_cmd_redraw()
	 */

	/* Combine and reorder the pack (later) */
	p_ptr->notice |= (PN_COMBINE | PN_REORDER);

	/* Update torch */
	p_ptr->update |= (PU_TORCH);

	/* Update stuff */
	p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
	                  PU_SANITY | PU_BODY);

	/* Forget and update view */
	p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);

	/* Redraw everything */
	p_ptr->redraw |= (PR_WIPE | PR_FRAME | PR_MAP);

	/* Hack -- update */
	handle_stuff();

	/* Redraw */
	Term_redraw();

	/* Refresh */
	Term_fresh();
}


/*
 * Redraw a term when it is resized
 */
void resize_window()
{
	/* Only if the dungeon exists */
	if (!character_dungeon)
	{
		return;
	}

	/* Hack -- Activate the Angband window for the redraw */
	Term_activate(angband_term[0]);

	/* Hack -- react to changes */
	Term_xtra_react();

	/* Window stuff */
	p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);

	/* Window stuff */
	p_ptr->window |= (PW_M_LIST | PW_MESSAGE | PW_OVERHEAD |
	                  PW_MONSTER | PW_OBJECT);


	/* Hack -- update */
	handle_stuff();

	/* Refresh */
	Term_fresh();
}




/*
 * Monster health description
 */
static const char *look_mon_desc(int m_idx)
{
	bool living = true;

	/* Determine if the monster is "living" (vs "undead") */
	monster_type *m_ptr = &m_list[m_idx];
	auto const r_ptr = m_ptr->race();
	if (r_ptr->flags & RF_UNDEAD) living = false;
	if (r_ptr->flags & RF_DEMON) living = false;
	if (r_ptr->flags & RF_NONLIVING) living = false;
	if (strchr("Egv", r_ptr->d_char)) living = false;


	/* Healthy monsters */
	if (m_ptr->hp >= m_ptr->maxhp)
	{
		/* No damage */
		return (living ? "unhurt" : "undamaged");
	}


	/* Calculate a health "percentage" */
	const int perc = 100L * m_ptr->hp / m_ptr->maxhp;

	if (perc >= 60)
	{
		return (living ? "somewhat wounded" : "somewhat damaged");
	}

	if (perc >= 25)
	{
		return (living ? "wounded" : "damaged");
	}

	if (perc >= 10)
	{
		return (living ? "badly wounded" : "badly damaged");
	}

	return (living ? "almost dead" : "almost destroyed");
}





/*** Targetting Code ***/


/*
 * Determine is a monster makes a reasonable target
 *
 * The concept of "targetting" was stolen from "Morgul" (?)
 *
 * The player can target any location, or any "target-able" monster.
 *
 * Currently, a monster is "target_able" if it is visible, and if
 * the player can hit it with a projection, and the player is not
 * hallucinating.  This allows use of "use closest target" macros.
 *
 * Future versions may restrict the ability to target "trappers"
 * and "mimics", but the semantics is a little bit weird.
 */
static bool target_able(int m_idx)
{
	auto const &r_info = game->edit_data.r_info;

	monster_type *m_ptr = &m_list[m_idx];

	/* Monster must be alive */
	if (!m_ptr->r_idx) return false;

	/* Monster must be visible */
	if (!m_ptr->ml) return false;

	/* Monster must be projectable */
	if (!projectable(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) return false;

	/* Hack -- no targeting hallucinations */
	if (p_ptr->image) return false;

	/* Dont target pets */
	if (is_friend(m_ptr) > 0) return false;

	/* Honor flag */
	if (r_info[m_ptr->r_idx].flags & RF_NO_TARGET) return false;

	/* XXX XXX XXX Hack -- Never target trappers */
	/* if (CLEAR_ATTR && (CLEAR_CHAR)) return false; */

	/* Assume okay */
	return true;
}




/*
 * Update (if necessary) and verify (if possible) the target.
 *
 * We return true if the target is "okay" and false otherwise.
 */
bool target_okay()
{
	/* Accept stationary targets */
	if (target_who < 0) return true;

	/* Check moving targets */
	if (target_who > 0)
	{
		/* Accept reasonable targets */
		if (target_able(target_who))
		{
			monster_type *m_ptr = &m_list[target_who];

			/* Acquire monster location */
			target_row = m_ptr->fy;
			target_col = m_ptr->fx;

			/* Good target */
			return true;
		}
	}

	/* Assume no target */
	return false;
}



/*
 * Hack -- help "select" a location (see below)
 */
static s16b target_pick(point p, int dy, int dx, std::vector<point> const &points)
{
	int b_i = -1, b_v = 9999;

	/* Scan the locations */
	for (std::size_t i = 0; i < points.size(); i++)
	{
		/* Point 2 */
		int x2 = points[i].x();
		int y2 = points[i].y();

		/* Directed distance */
		int x3 = (x2 - p.x());
		int y3 = (y2 - p.y());

		/* Verify quadrant */
		if (dx && (x3 * dx <= 0)) continue;
		if (dy && (y3 * dy <= 0)) continue;

		/* Absolute distance */
		int x4 = ABS(x3);
		int y4 = ABS(y3);

		/* Verify quadrant */
		if (dy && !dx && (x4 > y4)) continue;
		if (dx && !dy && (y4 > x4)) continue;

		/* Approximate Double Distance */
		int v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));

		/* XXX XXX XXX Penalize location */

		/* Track best */
		if ((b_i >= 0) && (v >= b_v)) continue;

		/* Track best */
		b_i = i;
		b_v = v;
	}

	/* Result */
	return (b_i);
}


/*
 * Hack -- determine if a given location is "interesting"
 */
static bool target_set_accept(int y, int x)
{
	auto const &r_info = game->edit_data.r_info;
	auto const &f_info = game->edit_data.f_info;

	/* Player grid is always interesting */
	if ((y == p_ptr->py) && (x == p_ptr->px)) return true;


	/* Handle hallucination */
	if (p_ptr->image) return false;


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

	/* Visible monsters */
	if (c_ptr->m_idx && c_ptr->m_idx < static_cast<int>(r_info.size()))
	{

		monster_type *m_ptr = &m_list[c_ptr->m_idx];
		/* Visible monsters */
		if (m_ptr->ml) return true;
	}

	/* Scan all objects in the grid */
	for (auto const this_o_idx: c_ptr->o_idxs)
	{
		/* Acquire object */
		object_type *o_ptr = &o_list[this_o_idx];

		/* Memorized object */
		if (o_ptr->marked)
		{
			return true;
		}
	}

	/* Interesting memorized features */
	if (c_ptr->info & (CAVE_MARK))
	{
		/* Hack -- Doors are boring */
		if (c_ptr->feat == FEAT_OPEN) return false;
		if (c_ptr->feat == FEAT_BROKEN) return false;
		if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
		                (c_ptr->feat <= FEAT_DOOR_TAIL)) return false;

		/* Accept 'naturally' interesting features */
		if (f_info[c_ptr->feat].flags & FF_NOTICE) return true;
	}

	/* Nope */
	return false;
}


/*
 * Prepare the "temp" array for "target_set"
 *
 * Return the number of target_able monsters in the set.
 */
static std::vector<point> target_set_prepare(int mode)
{
	std::vector<point> points;

	// Scan the current panel
	for (int y = panel_row_min; y <= panel_row_max; y++)
	{
		for (int x = panel_col_min; x <= panel_col_max; x++)
		{
			cave_type *c_ptr = &cave[y][x];

			// Require "interesting" contents
			if (!target_set_accept(y, x)) continue;

			// Require target_able monsters for "TARGET_KILL"
			if ((mode & (TARGET_KILL)) && !target_able(c_ptr->m_idx)) continue;

			// Save the location
			points.push_back(point(x,y));
		}
	}

	// Sort the points by distance to player; we'll
	// use a stable sort to avoid equidistant targets
	// "swapping" priorities.
	std::stable_sort(
		std::begin(points),
		std::end(points),
		[](point i, point j) -> bool {
			auto di = distance(p_ptr->py, p_ptr->px, i.y(), i.x());
			auto dj = distance(p_ptr->py, p_ptr->px, j.y(), j.x());
			return di < dj;
		}
	);

	return points;
}


bool target_object(int y, int x, int mode, const char *info, bool *boring,
                   object_type *o_ptr, char *out_val, const char **s1, const char **s2, const char **s3,
                   int *query)
{
	char o_name[80];

	/* Not boring */
	*boring = false;

	/* Obtain an object description */
	object_desc(o_name, o_ptr, true, 3);

	/* Describe the object */
	sprintf(out_val, "%s%s%s%s [%s]", *s1, *s2, *s3, o_name, info);
	prt(out_val, 0, 0);
	move_cursor_relative(y, x);
	*query = inkey();

	/* Always stop at "normal" keys */
	if ((*query != '\r') && (*query != '\n') && (*query != ' ')) return true;

	/* Sometimes stop at "space" key */
	if ((*query == ' ') && !(mode & (TARGET_LOOK))) return true;

	/* Change the intro */
	*s1 = "It is ";

	/* Plurals */
	if (o_ptr->number != 1) *s1 = "They are ";

	/* Preposition */
	*s2 = "on ";
	return false;
}

/*
 * Examine a grid, return a keypress.
 *
 * The "mode" argument contains the "TARGET_LOOK" bit flag, which
 * indicates that the "space" key should scan through the contents
 * of the grid, instead of simply returning immediately.  This lets
 * the "look" command get complete information, without making the
 * "target" command annoying.
 *
 * The "info" argument contains the "commands" which should be shown
 * inside the "[xxx]" text.  This string must never be empty, or grids
 * containing monsters will be displayed with an extra comma.
 *
 * Note that if a monster is in the grid, we update both the monster
 * recall info and the health bar info to track that monster.
 *
 * Eventually, we may allow multiple objects per grid, or objects
 * and terrain features in the same grid. XXX XXX XXX
 *
 * This function must handle blindness/hallucination.
 */
static int target_set_aux(int y, int x, int mode, const char *info_)
{
	auto const &d_info = game->edit_data.d_info;
	auto const &st_info = game->edit_data.st_info;
	auto const &wf_info = game->edit_data.wf_info;
	auto const &f_info = game->edit_data.f_info;
	auto const &k_info = game->edit_data.k_info;

	std::string info(info_);

	cave_type *c_ptr = &cave[y][x];

	const char *s1;
	const char *s2;
	const char *s3;

	bool boring;

	int feat;

	int query;

	char out_val[160];


	/* Repeat forever */
	while (true)
	{
		/* Paranoia */
		query = ' ';

		/* Assume boring */
		boring = true;

		/* Default */
		s1 = "You see ";
		s2 = "";
		s3 = "";

		/* Hack -- under the player */
		if ((y == p_ptr->py) && (x == p_ptr->px))
		{
			/* Description */
			s1 = "You are ";

			/* Preposition */
			s2 = "on ";
		}


		/* Hack -- hallucination */
		if (p_ptr->image)
		{
			const char *name = "something strange";

			/* Display a message */
			sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info.c_str());
			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			query = inkey();

			/* Stop on everything but "return" */
			if ((query != '\r') && (query != '\n')) break;

			/* Repeat forever */
			continue;
		}


		/* Actual monsters */
		if (c_ptr->m_idx)
		{
			monster_type *m_ptr = &m_list[c_ptr->m_idx];
			auto const r_ptr = m_ptr->race();

			/* Mimics special treatment -- looks like an object */
			if ((r_ptr->flags & RF_MIMIC) && (m_ptr->csleep))
			{
				/* Acquire object */
				object_type *o_ptr = &o_list[m_ptr->mimic_o_idx()];

				if (o_ptr->marked)
				{
					if (target_object(y, x, mode, info.c_str(), &boring, o_ptr, out_val, &s1, &s2, &s3, &query))
					{
						break;
					}
				}
			}
			else
			{
				/* Visible */
				if (m_ptr->ml)
				{
					bool recall = false;

					char m_name[80];

					/* Not boring */
					boring = false;

					/* Get the monster name ("a kobold") */
					monster_desc(m_name, m_ptr, 0x08);

					/* Hack -- track this monster race */
					monster_race_track(m_ptr->r_idx, m_ptr->ego);

					/* Hack -- health bar for this monster */
					health_track(c_ptr->m_idx);

					/* Hack -- handle stuff */
					handle_stuff();

					/* Interact */
					while (true)
					{
						/* Recall */
						if (recall)
						{
							/* Save */
							screen_save_no_flush();

							/* Recall on screen */
							screen_roff(m_ptr->r_idx, m_ptr->ego);

							/* Hack -- Complete the prompt (again) */
							Term_addstr( -1, TERM_WHITE, fmt::format("  [r,{}]", info).c_str());

							/* Command */
							query = inkey();

							/* Restore */
							screen_load_no_flush();
						}

						/* Normal */
						else
						{
							const char *mstat;

							switch (m_ptr->status)
							{
							case MSTATUS_NEUTRAL:
							case MSTATUS_NEUTRAL_M:
							case MSTATUS_NEUTRAL_P:
								mstat = " (neutral) ";
								break;
							case MSTATUS_PET:
								mstat = " (pet) ";
								break;
							case MSTATUS_FRIEND:
								mstat = " (coaligned) ";
								break;
							case MSTATUS_COMPANION:
								mstat = " (companion) ";
								break;
							default:
								mstat = " ";
								break;
							}
							if (m_ptr->mflag & MFLAG_PARTIAL) mstat = " (partial) ";

							/* Describe, and prompt for recall */
							sprintf(out_val, "%s%s%s%s (level %d, %s%s%s)%s%s[r,%s]",
							        s1, s2, s3, m_name,
							        m_ptr->level, look_mon_desc(c_ptr->m_idx),
								(m_ptr->csleep) ? ", asleep" : "",
							        (m_ptr->mflag & MFLAG_QUEST) ? ", quest" : "",
							        (m_ptr->smart & SM_CLONED ? " (clone)" : ""),
								(mstat),
								info.c_str());

							prt(out_val, 0, 0);

							/* Place cursor */
							move_cursor_relative(y, x);

							/* Command */
							query = inkey();
						}

						/* Normal commands */
						if (query != 'r') break;

						/* Toggle recall */
						recall = !recall;
					}

					/* Always stop at "normal" keys */
					if ((query != '\r') && (query != '\n') && (query != ' ')) break;

					/* Sometimes stop at "space" key */
					if ((query == ' ') && !(mode & (TARGET_LOOK))) break;

					/* Change the intro */
					s1 = "It is ";

					/* Hack -- take account of gender */
					if (r_ptr->flags & RF_FEMALE) s1 = "She is ";
					else if (r_ptr->flags & RF_MALE) s1 = "He is ";

					/* Use a preposition */
					s2 = "carrying ";

					/* Scan all objects being carried */
					std::size_t i = 0;
					for (; i < m_ptr->hold_o_idxs.size(); i++)
					{
						/* Acquire object */
						auto this_o_idx = m_ptr->hold_o_idxs.at(i);
						object_type *o_ptr = &o_list[this_o_idx];

						/* Obtain an object description */
						char o_name[80];
						object_desc(o_name, o_ptr, true, 3);

						/* Describe the object */
						sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, o_name, info.c_str());
						prt(out_val, 0, 0);
						move_cursor_relative(y, x);
						query = inkey();

						/* Always stop at "normal" keys */
						if ((query != '\r') && (query != '\n') && (query != ' '))
						{
							break;
						}

						/* Sometimes stop at "space" key */
						if ((query == ' ') && !(mode & (TARGET_LOOK)))
						{
							break;
						}

						/* Change the intro */
						s2 = "also carrying ";
					}

					/* Double break? */
					if (i != m_ptr->hold_o_idxs.size())
					{
						break;
					}

					/* Use a preposition */
					s2 = "on ";
				}
			}
		}

		/* Scan all objects in the grid */
		{
			std::size_t i = 0;
			for (; i < c_ptr->o_idxs.size(); i++)
			{
				/* Acquire object */
				auto this_o_idx = c_ptr->o_idxs.at(i);
				object_type *o_ptr = &o_list[this_o_idx];

				/* Describe it */
				if (o_ptr->marked && target_object(y, x, mode, info.c_str(), &boring, o_ptr, out_val, &s1, &s2, &s3, &query))
				{
					break;
				}
			}

			/* Double break? */
			if (i != c_ptr->o_idxs.size())
			{
				break;
			}
		}

		/* Feature (apply "mimic") */
		if (c_ptr->mimic)
		{
			feat = c_ptr->mimic;
		}
		else
		{
			feat = f_info[c_ptr->feat].mimic;
		}

		/* Require knowledge about grid, or ability to see grid */
		if (!(c_ptr->info & (CAVE_MARK)) && !player_can_see_bold(y, x))
		{
			/* Forget feature */
			feat = FEAT_NONE;
		}

		/* Terrain feature if needed */
		if (boring || (feat >= FEAT_GLYPH))
		{
			std::string name;

			/* Hack -- special handling for building doors */
			if (feat == FEAT_SHOP)
			{
				name = st_info[c_ptr->special].name;
			}
			else
			{
				name = f_info[feat].name;
			}

			/* Hack -- handle unknown grids */
			if (feat == FEAT_NONE) name = "unknown grid";

			/* Pick a prefix */
			if (*s2 &&
			                (((feat >= FEAT_MINOR_GLYPH) &&
					  (feat < FEAT_SHOP)) ||
			                 (feat == FEAT_DIRT) ||
			                 (feat == FEAT_GRASS) ||
			                 (feat == FEAT_FLOWER))) s2 = "on ";
			else if (*s2 && (feat == FEAT_SMALL_TREES)) s2 = "by ";
			else if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in ";

			/* Pick proper indefinite article */
			s3 = (is_a_vowel(name[0])) ? "an " : "a ";

			/* Hack -- special introduction for store & building doors */
			if (feat == FEAT_SHOP)
			{
				s3 = "the entrance to the ";
			}

			if ((feat == FEAT_MORE) && c_ptr->special)
			{
				s3 = "";
				name = d_info[c_ptr->special].text;
			}

			if (p_ptr->wild_mode && (feat == FEAT_TOWN))
			{
				auto const &wilderness = game->wilderness;
				auto const &wf = wf_info[wilderness(x, y).feat];

				s3 = "";
				name = fmt::format("{}({})", wf.name, wf.text);
			}

			if (feat == FEAT_FOUNTAIN)
			{
				int tv, sv;

				if (c_ptr->special <= SV_POTION_LAST)
				{
					tv = TV_POTION;
					sv = c_ptr->special;
				}
				else
				{
					tv = TV_POTION2;
					sv = c_ptr->special - SV_POTION_LAST;
				}

				info = k_info.at(lookup_kind(tv, sv))->name;
			}

			/* Display a message */
			if (!wizard)
			{
				sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name.c_str(), info.c_str());
			}
			else
			{
				sprintf(out_val, "%s%s%s%s [%s] (%d:%d:%d)",
					s1, s2, s3, name.c_str(), info.c_str(),
				        c_ptr->feat, c_ptr->mimic, c_ptr->special);
			}
			prt(out_val, 0, 0);
			move_cursor_relative(y, x);
			query = inkey();

			/* Always stop at "normal" keys */
			if ((query != '\r') && (query != '\n') && (query != ' ')) break;
		}

		/* Stop on everything but "return" */
		if ((query != '\r') && (query != '\n')) break;
	}

	/* Keep going */
	return (query);
}




/*
 * Handle "target" and "look".
 *
 * Note that this code can be called from "get_aim_dir()".
 *
 * All locations must be on the current panel.  Consider the use of
 * "panel_bounds()" to allow "off-panel" targets, perhaps by using
 * some form of "scrolling" the map around the cursor.  XXX XXX XXX
 * That is, consider the possibility of "auto-scrolling" the screen
 * while the cursor moves around.  This may require changes in the
 * "update_mon()" code to allow "visibility" even if off panel, and
 * may require dynamic recalculation of the "temp" grid set.
 *
 * Hack -- targetting/observing an "outer border grid" may induce
 * problems, so this is not currently allowed.
 *
 * The player can use the direction keys to move among "interesting"
 * grids in a heuristic manner, or the "space", "+", and "-" keys to
 * move through the "interesting" grids in a sequential manner, or
 * can enter "location" mode, and use the direction keys to move one
 * grid at a time in any direction.  The "t" (set target) command will
 * only target a monster (as opposed to a location) if the monster is
 * target_able and the "interesting" mode is being used.
 *
 * The current grid is described using the "look" method above, and
 * a new command may be entered at any time, but note that if the
 * "TARGET_LOOK" bit flag is set (or if we are in "location" mode,
 * where "space" has no obvious meaning) then "space" will scan
 * through the description of the current grid until done, instead
 * of immediately jumping to the next "interesting" grid.  This
 * allows the "target" command to retain its old semantics.
 *
 * The "*", "+", and "-" keys may always be used to jump immediately
 * to the next (or previous) interesting grid, in the proper mode.
 *
 * The "return" key may always be used to scan through a complete
 * grid description (forever).
 *
 * This command will cancel any old target, even if used from
 * inside the "look" command.
 */
bool target_set(int mode)
{
	int i, d, m;
	int y = p_ptr->py;
	int x = p_ptr->px;

	bool done = false;

	bool flag = true;

	char query;

	char info[80];

	int screen_wid, screen_hgt;
	int panel_wid, panel_hgt;

	/* Get size */
	get_screen_size(&screen_wid, &screen_hgt);

	/* Calculate the amount of panel movement */
	panel_hgt = screen_hgt / 2;
	panel_wid = screen_wid / 2;

	/* Cancel target */
	target_who = 0;


	/* Cancel tracking */
	/* health_track(0); */


	/* Prepare the "temp" array */
	std::vector<point> points = target_set_prepare(mode);

	/* Start near the player */
	m = 0;

	/* Interact */
	while (!done)
	{
		/* Interesting grids */
		if (flag && points.size())
		{
			y = points[m].y();
			x = points[m].x();

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

			/* Allow target */
			if (target_able(c_ptr->m_idx))
			{
				strcpy(info, "q,t,p,o,+,-,'dir'");
			}

			/* Dis-allow target */
			else
			{
				strcpy(info, "q,p,o,+,-,'dir'");
			}

			/* Describe and Prompt */
			query = target_set_aux(y, x, mode, info);

			/* Cancel tracking */
			/* health_track(0); */

			/* Assume no "direction" */
			d = 0;

			/* Analyze */
			switch (query)
			{
			case ESCAPE:
			case 'q':
				{
					done = true;
					break;
				}

			case 't':
			case '.':
			case '5':
			case '0':
				{
					if (target_able(c_ptr->m_idx))
					{
						health_track(c_ptr->m_idx);
						target_who = c_ptr->m_idx;
						target_row = y;
						target_col = x;
						done = true;
					}
					else
					{
						bell();
					}
					break;
				}

			case ' ':
			case '*':
			case '+':
				{
					if (++m == points.size())
					{
						m = 0;
					}
					break;
				}

			case '-':
				{
					if (m-- == 0)
					{
						m = points.size() - 1;
					}
					break;
				}

			case 'p':
				{
					/* Recenter the map around the player */
					verify_panel();

					/* Update stuff */
					p_ptr->update |= (PU_MONSTERS);

					/* Redraw map */
					p_ptr->redraw |= (PR_MAP);

					/* Window stuff */
					p_ptr->window |= (PW_OVERHEAD);

					/* Handle stuff */
					handle_stuff();

					/* Recalculate interesting grids */
					target_set_prepare(mode);

					y = p_ptr->py;
					x = p_ptr->px;

					/* Fall through... */
				}

			case 'o':
				{
					flag = false;
					break;
				}

			case 'm':
				{
					break;
				}

			default:
				{
					/* Extract the action (if any) */
					d = get_keymap_dir(query);

					if (!d) bell();
					break;
				}
			}

			/* Hack -- move around */
			if (d)
			{
				/* Find a new monster */
				i = target_pick(points[m], ddy[d], ddx[d], points);

				/* Scroll to find interesting grid */
				if (i < 0)
				{
					int dy;
					int dx;

					dy = ddy[d];
					dx = ddx[d];

					/* Note panel change */
					if (change_panel(dy, dx))
					{
						auto const t = points[m];

						/* Recalculate interesting grids */
						target_set_prepare(mode);

						/* Find a new monster */
						i = target_pick(t, dy, dx, points);

						/* Restore panel if needed */
						if (i < 0)
						{
							/* Restore panel */
							change_panel( -dy, -dx);

							/* Recalculate interesting grids */
							target_set_prepare(mode);
						}
					}
				}

				/* Use that grids */
				if (i >= 0) m = i;
			}
		}

		/* Arbitrary grids */
		else
		{
			/* Default prompt */
			strcpy(info, "q,t,p,m,+,-,'dir'");

			/* Describe and Prompt (enable "TARGET_LOOK") */
			query = target_set_aux(y, x, mode | TARGET_LOOK, info);

			/* Cancel tracking */
			/* health_track(0); */

			/* Assume no direction */
			d = 0;

			/* Analyze the keypress */
			switch (query)
			{
			case ESCAPE:
			case 'q':
				{
					done = true;
					break;
				}

			case 't':
			case '.':
			case '5':
			case '0':
				{
					target_who = -1;
					target_row = y;
					target_col = x;
					done = true;
					break;
				}

			case ' ':
			case '*':
			case '+':
			case '-':
				{
					break;
				}

			case 'p':
				{
					y = p_ptr->py;
					x = p_ptr->px;
				}

			case 'o':
				{
					break;
				}

			case 'm':
				{
					flag = true;
					break;
				}

			default:
				{
					/* Extract the action (if any) */
					d = get_keymap_dir(query);

					if (!d) bell();
					break;
				}
			}

			/* Handle "direction" */
			if (d)
			{
				int dy = ddy[d];
				int dx = ddx[d];

				/* Move */
				y += dy;
				x += dx;

				/* Do not move horizontally if unnecessary */
				if (((x < panel_col_min + panel_wid) && (dx > 0)) ||
				                ((x > panel_col_min + panel_wid) && (dx < 0)))
				{
					dx = 0;
				}

				/* Do not move vertically if unnecessary */
				if (((y < panel_row_min + panel_hgt) && (dy > 0)) ||
				                ((y > panel_row_min + panel_hgt) && (dy < 0)))
				{
					dy = 0;
				}
				/* Apply the motion */
				if ((y >= panel_row_min + screen_hgt) ||
				                (y < panel_row_min) ||
				                (x > panel_col_min + screen_wid) ||
				                (x < panel_col_min))
				{
					/* Change panel and recalculate interesting grids */
					if (change_panel(dy, dx)) target_set_prepare(mode);
				}

				/* Boundary checks */
				if (!wizard)
				{
					/* Hack -- Verify y */
					if (y <= 0) y = 1;
					else if (y >= cur_hgt - 1) y = cur_hgt - 2;

					/* Hack -- Verify x */
					if (x <= 0) x = 1;
					else if (x >= cur_wid - 1) x = cur_wid - 2;
				}
				else
				{
					/* Hack -- Verify y */
					if (y < 0) y = 0;
					else if (y > cur_hgt - 1) y = cur_hgt - 1;

					/* Hack -- Verify x */
					if (x < 0) x = 0;
					else if (x > cur_wid - 1) x = cur_wid - 1;
				}
			}
		}
	}

	/* Clear the top line */
	prt("", 0, 0);

	/* Recenter the map around the player */
	verify_panel();

	/* Update stuff */
	p_ptr->update |= (PU_MONSTERS);

	/* Redraw map */
	p_ptr->redraw |= (PR_MAP);

	/* Window stuff */
	p_ptr->window |= (PW_OVERHEAD);

	/* Handle stuff */
	handle_stuff();

	/* Failure to set target */
	if (!target_who) return false;

	/* Success */
	return true;
}



/*
 * Get an "aiming direction" from the user.
 *
 * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
 * "0" for "current target", and "-1" for "entry aborted".
 *
 * Note that "Force Target", if set, will pre-empt user interaction,
 * if there is a usable target already set.
 *
 * Note that confusion over-rides any (explicit?) user choice.
 */
bool get_aim_dir(int *dp)
{
	int dir;

	char command;

	const char *p;

	if (repeat_pull(dp))
	{
		/* Confusion? */

		/* Verify */
		if (!(*dp == 5 && !target_okay()))
		{
			return true;
		}
	}

	/* Initialize */
	(*dp) = 0;

	/* Global direction */
	dir = command_dir;

	/* Hack -- auto-target if requested */
	if (options->use_old_target && target_okay())
	{
		dir = 5;
	}

	/* Ask until satisfied */
	while (!dir)
	{
		/* Choose a prompt */
		if (!target_okay())
		{
			p = "Direction ('*' to choose a target, Escape to cancel)? ";
		}
		else
		{
			p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
		}

		/* Get a command (or Cancel) */
		if (!get_com(p, &command)) break;

		/* Convert various keys to "standard" keys */
		switch (command)
		{
			/* Use current target */
		case 'T':
		case 't':
		case '.':
		case '5':
		case '0':
			{
				dir = 5;
				break;
			}

			/* Set new target */
		case '*':
			{
				if (target_set(TARGET_KILL)) dir = 5;
				break;
			}

		default:
			{
				/* Extract the action (if any) */
				dir = get_keymap_dir(command);

				break;
			}
		}

		/* Verify requested targets */
		if ((dir == 5) && !target_okay()) dir = 0;

		/* Error */
		if (!dir) bell();
	}

	/* No direction */
	if (!dir) return false;

	/* Save the direction */
	command_dir = dir;

	/* Check for confusion */
	if (p_ptr->confused)
	{
		/* XXX XXX XXX */
		/* Random direction */
		dir = ddd[rand_int(8)];
	}

	/* Notice confusion */
	if (command_dir != dir)
	{
		/* Warn the user */
		msg_print("You are confused.");
	}

	/* Save direction */
	(*dp) = dir;


	repeat_push(dir);

	/* A "valid" direction was entered */
	return true;
}



/*
 * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
 * and place it into "command_dir", unless we already have one.
 *
 * This function should be used for all "repeatable" commands, such as
 * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
 * as all commands which must reference a grid adjacent to the player,
 * and which may not reference the grid under the player.  Note that,
 * for example, it is no longer possible to "disarm" or "open" chests
 * in the same grid as the player.
 *
 * Direction "5" is illegal and will (cleanly) abort the command.
 *
 * This function tracks and uses the "global direction", and uses
 * that as the "desired direction", to which "confusion" is applied.
 */
bool get_rep_dir(int *dp)
{
	int dir;

	if (repeat_pull(dp))
	{
		return true;
	}

	/* Initialize */
	(*dp) = 0;

	/* Global direction */
	dir = command_dir;

	/* Get a direction */
	while (!dir)
	{
		char ch;

		/* Get a command (or Cancel) */
		if (!get_com("Direction (Escape to cancel)? ", &ch)) break;

		/* Look up the direction */
		dir = get_keymap_dir(ch);

		/* Oops */
		if (!dir) bell();
	}

	/* Prevent weirdness */
	if (dir == 5) dir = 0;

	/* Aborted */
	if (!dir) return false;

	/* Save desired direction */
	command_dir = dir;

	/* Apply "confusion" */
	if (p_ptr->confused)
	{
		/* Standard confusion */
		if (rand_int(100) < 75)
		{
			/* Random direction */
			dir = ddd[rand_int(8)];
		}
	}

	/* Notice confusion */
	if (command_dir != dir)
	{
		/* Warn the user */
		msg_print("You are confused.");
	}

	/* Save direction */
	(*dp) = dir;


	repeat_push(dir);

	/* Success */
	return true;
}


/*
 * old -- from PsiAngband.
 */
bool tgt_pt(int *x, int *y)
{
	bool success = false;

	// Make the cursor visible
	Term_with_saved_cursor_flags([&success, &x, &y]() {

		*x = p_ptr->px;
		*y = p_ptr->py;

		/* Get size */
		int screen_wid;
		int screen_hgt;
		get_screen_size(&screen_wid, &screen_hgt);

		// Make cursor visible
		Term_show_cursor();

		// Prompt
		msg_print("Select a point and press space.");

		char ch = '\0';
		while ((ch != 27) && (ch != ' '))
		{
			move_cursor_relative(*y, *x);
			ch = inkey();
			switch (ch)
			{
			case 27:
				break;
			case ' ':
				success = true;
				break;
			default:
				/* Look up the direction */
				int d;
				d = get_keymap_dir(ch);

				if (!d) break;

				*x += ddx[d];
				*y += ddy[d];

				/* Hack -- Verify x */
				if ((*x >= cur_wid - 1) || (*x >= panel_col_min + screen_wid)) (*x)--;
				else if ((*x <= 0) || (*x <= panel_col_min)) (*x)++;

				/* Hack -- Verify y */
				if ((*y >= cur_hgt - 1) || (*y >= panel_row_min + screen_hgt)) (*y)--;
				else if ((*y <= 0) || (*y <= panel_row_min)) (*y)++;

				break;
			}
		}
	});

	// Refresh
	Term_fresh();
	return success;
}

/*
 * Set "p_ptr->grace", notice observable changes
 */
void set_grace(s32b v)
{
	if (v < -300000) v = -300000;
	if (v > 300000) v = 300000;
	p_ptr->grace = v;
	p_ptr->update |= PU_BONUS;
	p_ptr->redraw |= (PR_FRAME);
	handle_stuff();
}

static bool test_object_wish(char *name, object_type *o_ptr, object_type *forge)
{
	auto &k_info = game->edit_data.k_info;
	auto const &e_info = game->edit_data.e_info;

	int save_aware;
	char buf[200];

	for (auto &k_entry: k_info)
	{
		auto k_ptr = k_entry.second;

		o_ptr = forge;

		if (k_ptr->flags & TR_NORM_ART) continue;
		if (k_ptr->flags & TR_INSTA_ART) continue;
		if (k_ptr->tval == TV_GOLD) continue;

		object_prep(o_ptr, k_entry.first);
		o_ptr->name1 = 0;
		o_ptr->name2 = 0;
		apply_magic(o_ptr, dun_level, false, false, false);
		/* Hack : aware status must be restored after describing the item name */
		save_aware = k_ptr->aware;
		object_aware(o_ptr);
		object_known(o_ptr);
		object_desc(buf, o_ptr, false, 0);
		strlower(buf);
		k_ptr->aware = save_aware;

		if (iequals(buf, name))
		{
			/* Don't search any more */
			return true;
		}

		if (strstr(name, buf) ||
		   /* Hack hack hackery */
		   (o_ptr->tval == TV_ROD_MAIN && strstr(name, "rod of")))
		{
			/* try all ego */
			for (std::size_t j = 1; j < e_info.size(); j++)
			{
				auto e_ptr = &e_info[j];
				bool ok = false;

				if (j && e_ptr->name.empty())
				{
					continue;
				}

				/* Must have the correct fields */
				if (j)
				{
					int z;

					for (z = 0; z < 6; z++)
					{
						if (e_ptr->tval[z] == k_ptr->tval)
						{
							if ((e_ptr->min_sval[z] <= k_ptr->sval) &&
							                (e_ptr->max_sval[z] >= k_ptr->sval)) ok = true;
						}
						if (ok) break;
					}
					if (!ok)
					{
						continue;
					}
				}
				do {
					object_prep(o_ptr, k_entry.first);
					o_ptr->name1 = 0;
					o_ptr->name2 = j;
					o_ptr->name2b = 0;
					apply_magic(o_ptr, dun_level, false, true, false);
					object_aware(o_ptr);
					object_known(o_ptr);
					object_desc(buf, o_ptr, false, 0);
					strlower(buf);
				} while (o_ptr->name2b || !o_ptr->artifact_name.empty()); //If apply magic added a second ego or made an artifact, retry.;

				if (iequals(buf, name))
				{
					/* Don't search any more */
					return true;
				}

				/* Restore again the aware status */
				k_ptr->aware = save_aware;

				if (strstr(name, buf))
				{
					/* try all ego */
					for (std::size_t jb = 0; jb < e_info.size(); jb++)
					{
						auto eb_ptr = &e_info[jb];
						bool ok = false;

						if (jb && eb_ptr->name.empty())
						{
						continue;
						}

						/* Must have the correct fields */
						if (jb)
						{
							int z;

							for (z = 0; z < 6; z++)
							{
								if (eb_ptr->tval[z] == k_ptr->tval)
								{
									if ((eb_ptr->min_sval[z] <= k_ptr->sval) &&
									                (eb_ptr->max_sval[z] >= k_ptr->sval)) ok = true;
								}
								if (ok) break;
							}
							if (!ok)
							{
								continue;
							}
						}

						do {
							object_prep(o_ptr, k_entry.first);
							o_ptr->name1 = 0;
							o_ptr->name2 = j;
							o_ptr->name2b = jb;
							apply_magic(o_ptr, dun_level, false, true, true);
							object_aware(o_ptr);
							object_known(o_ptr);
							object_desc(buf, o_ptr, false, 0);
							strlower(buf);
						} while (!o_ptr->artifact_name.empty()); //If apply magic turns it into an artifact, retry.
	
						if (iequals(buf, name))
						{
							/* Don't search any more */
							return true;
						}
						else
						{
							/* Restore again the aware status */
							k_ptr->aware = save_aware;
						}
					}
				}
			}
		}
	}
	return false;
}

static void clean_wish_name(char *buf, char *name)
{
	char *p;
	int i, j;

	/* Lowercase the wish */
	strlower(buf);

	/* Nuke uneccesary spaces */
	p = buf;
	while (*p == ' ') p++;
	i = 0;
	j = 0;
	while (p[i])
	{
		if ((p[i] == ' ') && (p[i + 1] == ' '))
		{
			i++;
			continue;
		}
		name[j++] = p[i++];
	}
	name[j++] = '\0';
	if (j)
	{
		j--;
		while (j && (name[j] == ' '))
		{
			name[j] = '\0';
			j--;
		}
	}
}

/*
 * Allow the player to make a wish
 */
bool make_wish()
{
	auto const &re_info = game->edit_data.re_info;
	auto const &r_info = game->edit_data.r_info;

	char name[200], *mname;
	int mstatus = MSTATUS_ENEMY;
	object_type forge, *o_ptr = &forge;

	/* Make an empty string */
	char buf[200];
	buf[0] = 0;

	/* Ask for the wish */
	if (!get_string("Wish for what? ", buf, 80)) return false;

	clean_wish_name(buf, name);

	/* You can't wish for a wish! */
	if (strstr(name, "wish"))
	{
		msg_print("You can't wish for a wish!");
		return false;
	}

	if (test_object_wish(name, o_ptr, &forge))
	{
		msg_print("Your wish becomes truth!");

		/* Give it to the player */
		drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);

		return true;
	}

	/* try monsters */
	if (starts_with(name, "enemy "))
	{
		mstatus = MSTATUS_ENEMY;
		mname = name + 6;
	}
	else if (starts_with(name, "neutral "))
	{
		mstatus = MSTATUS_NEUTRAL;
		mname = name + 8;
	}
	else if (starts_with(name, "friendly "))
	{
		mstatus = MSTATUS_FRIEND;
		mname = name + 9;
	}
	else if (starts_with(name, "pet "))
	{
		mstatus = MSTATUS_PET;
		mname = name + 4;
	}
	else if (starts_with(name, "companion "))
	{
		if (can_create_companion()) mstatus = MSTATUS_COMPANION;
		else mstatus = MSTATUS_PET;
		mname = name + 10;
	}
	else
	{
		mname = name;
	}

	for (std::size_t i = 1; i < r_info.size(); i++)
	{
		auto r_ptr = &r_info[i];

		if (!r_ptr->name) continue;

		if (r_ptr->flags & RF_SPECIAL_GENE) continue;
		if (r_ptr->flags & RF_NEVER_GENE) continue;
		if (r_ptr->flags & RF_UNIQUE) continue;

		sprintf(buf, "%s", r_ptr->name);
		strlower(buf);

		if (strstr(mname, buf))
		{
			/* try all ego */
			for (std::size_t j = 0; j < re_info.size(); j++)
			{
				auto re_ptr = &re_info[j];

				if (j && !re_ptr->name) continue;

				if (!mego_ok(r_ptr, j)) continue;

				if (j)
				{
					if (re_ptr->before)
					{
						sprintf(buf, "%s %s", re_ptr->name, r_ptr->name);
					}
					else
					{
						sprintf(buf, "%s %s", r_ptr->name, re_ptr->name);
					}
				}
				else
				{
					sprintf(buf, "%s", r_ptr->name);
				}

				strlower(buf);

				if (iequals(mname, buf))
				{
					int wy = p_ptr->py, wx = p_ptr->px;
					int attempts = 100;

					do
					{
						scatter(&wy, &wx, p_ptr->py, p_ptr->px, 5);
					}
					while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);

					/* Create the monster */
					if (place_monster_one(wy, wx, i, j, false, mstatus))
					{
						msg_print("Your wish becomes truth!");
					}

					/* Don't search any more */
					return true;
				}
			}
		}
	}
	return false;
}


/*
 * Corrupted have a 1/3 chance of losing a mutation each time this is called, 
 * assuming they have any in the first place
 */
static void corrupt_corrupted()
{
	if (magik(45))
	{
		lose_corruption();
	}
	else
	{
		gain_random_corruption();
	}
}

/*
 * Change to an other subrace
 */
void switch_subrace(std::size_t racem, bool copy_old)
{
	auto &race_mod_info = game->edit_data.race_mod_info;

	assert(racem < race_mod_info.size());

	/* If we switch to the saved subrace, we copy over the old subrace data */
	if (copy_old && (racem == SUBRACE_SAVE))
	{
		// Keep old description
		auto old_desc = race_mod_info[SUBRACE_SAVE].description;
		// Copy everything
		race_mod_info[SUBRACE_SAVE] = race_mod_info[p_ptr->pracem];
		// Reinstate description
		race_mod_info[SUBRACE_SAVE].description = old_desc;
	}

	p_ptr->pracem = racem;
	rmp_ptr = &race_mod_info[p_ptr->pracem];
}

/*
 * Rebirth, recalc hp & exp/level
 */
void do_rebirth()
{
	/* Experience factor */
	p_ptr->expfact = rp_ptr->ps.exp + rmp_ptr->ps.exp + cp_ptr->ps.exp;

	/* Hitdice */
	p_ptr->hitdie = rp_ptr->ps.mhp + rmp_ptr->ps.mhp + cp_ptr->ps.mhp;

	/* Recalc HP */
	do_cmd_rerate();

	/* Change the level if needed */
	check_experience();
	p_ptr->max_plv = p_ptr->lev;

	/* Redraw/calc stuff */
	p_ptr->redraw |= (PR_FRAME);
	p_ptr->update |= (PU_BONUS);
	handle_stuff();

	lite_spot(p_ptr->py, p_ptr->px);
}
