/* File: cmd6.c */

/* Purpose: Object commands */

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

#include "angband.h"


/*
 * Forward declare
 */
static bool_ activate_spell(object_type * o_ptr, byte choice);


/*
 * General function to find an item by its name
 */
cptr get_item_hook_find_obj_what;
bool_ get_item_hook_find_obj(int *item)
{
	int i;
	char buf[80];
	char buf2[100];

	strcpy(buf, "");
	if (!get_string(get_item_hook_find_obj_what, buf, 79))
		return FALSE;

	for (i = 0; i < INVEN_TOTAL; i++)
	{
		object_type *o_ptr = &p_ptr->inventory[i];

		if (!item_tester_okay(o_ptr)) continue;

		object_desc(buf2, o_ptr, -1, 0);
		if (!strcmp(buf, buf2))
		{
			*item = i;
			return TRUE;
		}
	}
	return FALSE;
}


/*
 * This file includes code for eating food, drinking potions,
 * reading scrolls, aiming wands, using staffs, zapping rods,
 * and activating artifacts.
 *
 * In all cases, if the player becomes "aware" of the item's use
 * by testing it, mark it as "aware" and reward some experience
 * based on the object's level, always rounding up.  If the player
 * remains "unaware", mark that object "kind" as "tried".
 *
 * This code now correctly handles the unstacking of wands, staffs,
 * and rods.  Note the overly paranoid warning about potential pack
 * overflow, which allows the player to use and drop a stacked item.
 *
 * In all "unstacking" scenarios, the "used" object is "carried" as if
 * the player had just picked it up.  In particular, this means that if
 * the use of an item induces pack overflow, that item will be dropped.
 *
 * For simplicity, these routines induce a full "pack reorganization"
 * which not only combines similar items, but also reorganizes various
 * items to obey the current "sorting" method.  This may require about
 * 400 item comparisons, but only occasionally.
 *
 * There may be a BIG problem with any "effect" that can cause "changes"
 * to the p_ptr->inventory.  For example, a "scroll of recharging" can cause
 * a wand/staff to "disappear", moving the p_ptr->inventory up.  Luckily, the
 * scrolls all appear BEFORE the staffs/wands, so this is not a problem.
 * But, for example, a "staff of recharging" could cause MAJOR problems.
 * In such a case, it will be best to either (1) "postpone" the effect
 * until the end of the function, or (2) "change" the effect, say, into
 * giving a staff "negative" charges, or "turning a staff into a stick".
 * It seems as though a "rod of recharging" might in fact cause problems.
 * The basic problem is that the act of recharging (and destroying) an
 * item causes the inducer of that action to "move", causing "o_ptr" to
 * no longer point at the correct item, with horrifying results.
 *
 * Note that food/potions/scrolls no longer use bit-flags for effects,
 * but instead use the "sval" (which is also used to sort the objects).
 */


/*
 * Determine the effects of eating a corpse. A corpse can be
 * eaten whole or cut into pieces for later.
 */
static void corpse_effect(object_type *o_ptr, bool_ cutting)
{
	monster_race *r_ptr = &r_info[o_ptr->pval2];

	/* Assume no bad effects */
	bool_ harmful = FALSE;

	byte method, effect, d_dice, d_side;

	int i, dam, idam = 0, mdam, brpow, brdam = 0;


	/* How much of the monster's breath attack remains */
	if (o_ptr->pval <= r_ptr->weight)
	{
		brpow = 0;
	}
	else
	{
		brpow = (o_ptr->pval - r_ptr->weight) / 5;
		if (brpow > (r_ptr->weight / 5)) brpow = r_ptr->weight / 5;
	}

	if (o_ptr->weight <= 0) o_ptr->weight = 1;
	if (o_ptr->pval <= 0) o_ptr->pval = 1;

	/*
	 * The breath is only discharged by accident or by slicing off pieces
	 * of meat, and only by corpses.
	 */
	if ((o_ptr->sval != SV_CORPSE_CORPSE) ||
	                (rand_int(o_ptr->weight / 5) && !cutting)) brpow = 0;

	/* Immediate effects - poison, acid, fire, etc. */
	if (!cutting)
	{
		for (i = 0; i < 4; i++)
		{
			/* skip empty blow slot */
			if (!r_ptr->blow[i].method) continue;

			method = r_ptr->blow[i].method;
			effect = r_ptr->blow[i].effect;
			d_dice = r_ptr->blow[i].d_dice;
			d_side = r_ptr->blow[i].d_side;
			dam = damroll(d_dice, d_side) * o_ptr->pval / o_ptr->weight / 2;
			idam = damroll(d_dice, d_side) *
			       ((o_ptr->weight / o_ptr->pval > 2) ?
			        o_ptr->weight / o_ptr->pval : 2);
			mdam = maxroll(d_dice, d_side) * 2;

			/* Analyse method */
			switch (method)
			{
				/* Methods that are meaningless after death */
			case RBM_BITE:
			case RBM_STING:
			case RBM_ENGULF:
			case RBM_DROOL:
			case RBM_SPIT:
			case RBM_GAZE:
			case RBM_WAIL:
			case RBM_BEG:
			case RBM_INSULT:
			case RBM_MOAN:
				{
					continue;
				}
			}

			/* Analyse effect */
			switch (effect)
			{
				/* Effects that are meaningless after death */
			case RBE_HURT:
			case RBE_UN_BONUS:
			case RBE_UN_POWER:
			case RBE_EAT_GOLD:
			case RBE_EAT_ITEM:
			case RBE_EAT_FOOD:
			case RBE_EAT_LITE:
			case RBE_ELEC:
			case RBE_COLD:
			case RBE_SHATTER:
				{
					break;
				}

			case RBE_POISON:
				{
					if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
					{
						set_poisoned(p_ptr->poisoned + dam + idam + 10);
						harmful = TRUE;
					}

					break;
				}

			case RBE_ACID:
				{
					/* Total Immunity */
					if (!(p_ptr->immune_acid || (dam <= 0)))
					{
						/* Resist the damage */
						if (p_ptr->resist_acid) dam = (dam + 2) / 3;
						if (p_ptr->oppose_acid) dam = (dam + 2) / 3;

						/* Take damage */
						take_hit(dam, "acidic food");
						harmful = TRUE;
					}
					else
					{
						set_oppose_acid(p_ptr->oppose_acid + idam);
					}

					break;
				}

			case RBE_FIRE:
				{
					/* Totally immune */
					if (p_ptr->immune_fire || (dam <= 0))
					{
						/* Resist the damage */
						if (p_ptr->resist_fire) dam = (dam + 2) / 3;
						if (p_ptr->oppose_fire) dam = (dam + 2) / 3;

						/* Take damage */
						take_hit(dam, "a fiery meal");
						harmful = TRUE;
					}
					else
					{
						set_oppose_fire(p_ptr->oppose_fire + idam);
					}

					break;
				}

			case RBE_BLIND:
				{
					if (!p_ptr->resist_blind)
					{
						set_blind(p_ptr->blind + dam * 2 + idam * 2 + 20);
					}

					break;
				}

			case RBE_CONFUSE:
				{
					if (!p_ptr->resist_conf)
					{
						set_confused(p_ptr->confused + dam + idam + 10);
					}
					if (!p_ptr->resist_chaos && rand_int(mdam - dam))
					{
						set_image(p_ptr->image + dam * 10 + idam * 10 + 100);
					}

					break;
				}

			case RBE_HALLU:
				{
					if (!p_ptr->resist_chaos && rand_int(mdam - dam))
					{
						set_image(p_ptr->image + dam * 10 + idam * 10 + 50);
					}

					break;
				}

			case RBE_TERRIFY:
				{
					if (!p_ptr->resist_fear)
					{
						set_afraid(p_ptr->afraid + dam + idam + 10);
					}

					break;
				}

			case RBE_PARALYZE:
				{
					if (!p_ptr->free_act)
					{
						set_paralyzed(p_ptr->paralyzed + dam + idam + 10);
					}

					break;
				}

			case RBE_LOSE_STR:
				{
					do_dec_stat(A_STR, STAT_DEC_NORMAL);

					break;
				}

			case RBE_LOSE_INT:
				{
					do_dec_stat(A_INT, STAT_DEC_NORMAL);

					break;
				}

			case RBE_LOSE_WIS:
				{
					do_dec_stat(A_WIS, STAT_DEC_NORMAL);

					break;
				}

			case RBE_LOSE_DEX:
				{
					do_dec_stat(A_DEX, STAT_DEC_NORMAL);

					break;
				}

			case RBE_LOSE_CON:
				{
					do_dec_stat(A_CON, STAT_DEC_NORMAL);

					break;
				}

			case RBE_LOSE_CHR:
				{
					do_dec_stat(A_CHR, STAT_DEC_NORMAL);

					break;
				}

				/* Don't eat Morgoth's corpse :) */
			case RBE_LOSE_ALL:
				{
					do_dec_stat(A_STR, STAT_DEC_NORMAL);
					do_dec_stat(A_INT, STAT_DEC_NORMAL);
					do_dec_stat(A_WIS, STAT_DEC_NORMAL);
					do_dec_stat(A_DEX, STAT_DEC_NORMAL);
					do_dec_stat(A_CON, STAT_DEC_NORMAL);
					do_dec_stat(A_CHR, STAT_DEC_NORMAL);
					o_ptr->pval = 1;

					break;
				}

			case RBE_SANITY:
				{
					msg_print("You feel your sanity slipping away!");
					take_sanity_hit(dam, "eating an insane monster");

					break;
				}

				/* Unlife is bad to eat */
			case RBE_EXP_10:
				{
					msg_print("A black aura surrounds the corpse!");

					if (p_ptr->hold_life && (rand_int(100) < 50))
					{
						msg_print("You keep hold of your life force!");
					}
					else
					{
						s32b d = damroll(10, 6) +
						         (p_ptr->exp / 100) * MON_DRAIN_LIFE;

						if (p_ptr->hold_life)
						{
							msg_print("You feel your life slipping away!");
							lose_exp(d / 10);
						}
						else
						{
							msg_print("You feel your life draining away!");
							lose_exp(d);
						}
					}

					o_ptr->pval = 1;

					break;
				}

			case RBE_EXP_20:
				{
					msg_print("A black aura surrounds the corpse!");

					if (p_ptr->hold_life && (rand_int(100) < 50))
					{
						msg_print("You keep hold of your life force!");
					}
					else
					{
						s32b d = damroll(20, 6) +
						         (p_ptr->exp / 100) * MON_DRAIN_LIFE;

						if (p_ptr->hold_life)
						{
							msg_print("You feel your life slipping away!");
							lose_exp(d / 10);
						}
						else
						{
							msg_print("You feel your life draining away!");
							lose_exp(d);
						}
					}

					o_ptr->pval = 1;

					break;
				}

			case RBE_EXP_40:
				{
					msg_print("A black aura surrounds the corpse!");

					if (p_ptr->hold_life && (rand_int(100) < 50))
					{
						msg_print("You keep hold of your life force!");
					}
					else
					{
						s32b d = damroll(40, 6) +
						         (p_ptr->exp / 100) * MON_DRAIN_LIFE;

						if (p_ptr->hold_life)
						{
							msg_print("You feel your life slipping away!");
							lose_exp(d / 10);
						}
						else
						{
							msg_print("You feel your life draining away!");
							lose_exp(d);
						}
					}

					o_ptr->pval = 1;

					break;
				}

			case RBE_EXP_80:
				{
					msg_print("A black aura surrounds the corpse!");

					if (p_ptr->hold_life && (rand_int(100) < 50))
					{
						msg_print("You keep hold of your life force!");
					}
					else
					{
						s32b d = damroll(80, 6) +
						         (p_ptr->exp / 100) * MON_DRAIN_LIFE;

						if (p_ptr->hold_life)
						{
							msg_print("You feel your life slipping away!");
							lose_exp(d / 10);
						}
						else
						{
							msg_print("You feel your life draining away!");
							lose_exp(d);
						}
					}

					o_ptr->pval = 1;

					break;
				}
			}
		}
	} /* if (!cutting) */


	/*
	 * The organ that supplies breath attacks is not
	 * immediately emptied upon death, although some types
	 * of breath have no effect.
	 * AMHD's make rather risky meals, and deadly snacks.
	 */

	/* Acid */
	if (r_ptr->flags4 & RF4_BR_ACID && brpow > 0)
	{
		brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));

		msg_print("You are hit by a gush of acid!");

		/* Total Immunity */
		if (!(p_ptr->immune_acid || (brdam <= 0)))
		{
			/* Take damage */
			acid_dam(brdam, "a gush of acid");
			harmful = TRUE;
		}
		o_ptr->pval = 1;
	}
	else if (r_ptr->flags4 & RF4_BR_ACID)
	{
		set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
	}

	/* Electricity */
	if (r_ptr->flags4 & RF4_BR_ELEC && brpow > 0)
	{
		brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));

		msg_print("You receive a heavy shock!");

		/* Total Immunity */
		if (!(p_ptr->immune_elec || (brdam <= 0)))
		{
			/* Take damage */
			elec_dam(brdam, "an electric shock");
			harmful = TRUE;
		}
		o_ptr->weight = o_ptr->weight - brpow;
		o_ptr->pval = o_ptr->weight;
	}
	else if (r_ptr->flags4 & RF4_BR_ELEC)
	{
		set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
	}

	/* Fire */
	if (r_ptr->flags4 & RF4_BR_FIRE && brpow > 0)
	{
		brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));

		msg_print("Roaring flames engulf you!");

		/* Total Immunity */
		if (!(p_ptr->immune_fire || (brdam <= 0)))
		{
			/* Take damage */
			fire_dam(brdam, "an explosion");
			harmful = TRUE;
		}
		o_ptr->pval = 1;
	}
	else if (r_ptr->flags4 & RF4_BR_FIRE)
	{
		set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
	}

	/* Cold */
	if (r_ptr->flags4 & RF4_BR_COLD && brpow > 0)
	{
		brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3));

		msg_print("You are caught in a freezing liquid!");

		/* Total Immunity */
		if (!(p_ptr->immune_cold || (brdam <= 0)))
		{
			/* Take damage */
			cold_dam(brdam, "a chilling blast");
			harmful = TRUE;
		}
		o_ptr->weight = o_ptr->weight - brpow;
		o_ptr->pval = o_ptr->weight;
	}
	else if (r_ptr->flags4 & RF4_BR_COLD)
	{
		set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
	}

	/* Poison */
	if (r_ptr->flags4 & RF4_BR_POIS && brpow > 0)
	{
		brdam = ((brpow / 3) > 800 ? 800 : (brpow / 3));

		msg_print("You are surrounded by toxic gases!");

		/* Resist the damage */
		if (p_ptr->resist_pois) brdam = (brdam + 2) / 3;
		if (p_ptr->oppose_pois) brdam = (brdam + 2) / 3;

		if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
		{
			(void)set_poisoned(p_ptr->poisoned + rand_int(brdam) + 10);
		}

		/* Take damage */
		take_hit(brdam, "toxic gases");
		o_ptr->weight = o_ptr->weight - brpow;
		o_ptr->pval = o_ptr->weight;
		harmful = TRUE;
	}

	/* Nether */
	if (r_ptr->flags4 & RF4_BR_NETH && brpow > 0)
	{
		brdam = ((brpow / 6) > 550 ? 550 : (brpow / 6));

		msg_print("A black aura surrounds the corpse!");

		if (p_ptr->resist_neth)
		{
			brdam *= 6;
			brdam /= (randint(6) + 6);
		}
		else
		{
			if (p_ptr->hold_life && (rand_int(100) < 75))
			{
				msg_print("You keep hold of your life force!");
			}
			else if (p_ptr->hold_life)
			{
				msg_print("You feel your life slipping away!");
				lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
			}
			else
			{
				msg_print("You feel your life draining away!");
				lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
			}
		}

		/* Take damage */
		take_hit(brdam, "an unholy blast");
		harmful = TRUE;
		o_ptr->weight = o_ptr->weight - brpow;
		o_ptr->pval = o_ptr->weight;
	}

	/* Confusion */
	if (r_ptr->flags4 & RF4_BR_CONF && brpow > 0)
	{
		msg_print("A strange liquid splashes on you!");

		if (!p_ptr->resist_conf)
		{
			set_confused(p_ptr->confused + brdam + idam + 10);
		}
		o_ptr->weight = o_ptr->weight - brpow;
		o_ptr->pval = o_ptr->weight;
	}

	/* Chaos */
	if (r_ptr->flags4 & RF4_BR_CHAO && brpow > 0)
	{
		brdam = ((brpow / 6) > 600 ? 600 : (brpow / 6));

		msg_print("A swirling cloud surrounds you!");

		if (p_ptr->resist_chaos)
		{
			brdam *= 6;
			brdam /= (randint(6) + 6);
		}

		if (!p_ptr->resist_conf)
		{
			(void)set_confused(p_ptr->confused + rand_int(20) + 10);
		}

		if (!p_ptr->resist_chaos)
		{
			(void)set_image(p_ptr->image + randint(10));
		}

		if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
		{
			if (p_ptr->hold_life && (rand_int(100) < 75))
			{
				msg_print("You keep hold of your life force!");
			}
			else if (p_ptr->hold_life)
			{
				msg_print("You feel your life slipping away!");
				lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
			}
			else
			{
				msg_print("You feel your life draining away!");
				lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
			}
		}

		/* Take damage */
		take_hit(brdam, "chaotic forces");
		o_ptr->pval = 1;
	}

	/* Disenchantment */
	if (r_ptr->flags4 & RF4_BR_DISE && brpow > 0)
	{
		brdam = ((brpow / 6) > 500 ? 500 : (brpow / 6));

		msg_print("You are blasted by raw mana!");

		if (p_ptr->resist_disen)
		{
			brdam *= 6;
			brdam /= (randint(6) + 6);
		}
		else
		{
			(void)apply_disenchant(0);
		}

		/* Take damage */
		take_hit(brdam, "raw mana");
		o_ptr->pval = 1;
	}

	/* Plasma */
	if (r_ptr->flags4 & RF4_BR_PLAS && brpow > 0)
	{
		brdam = ((brpow / 6) > 150 ? 150 : (brpow / 6));

		msg_print("Searing flames engulf the corpse!");

		/* Resist the damage */
		if (p_ptr->resist_fire || p_ptr->oppose_fire) brdam = (brdam + 2) / 3;

		if (!p_ptr->resist_sound)
		{
			int k = (randint((brdam > 40) ? 35 : (brdam * 3 / 4 + 5)));
			(void)set_stun(p_ptr->stun + k);
		}

		/* Take damage */
		take_hit(brdam, "an explosion");
		harmful = TRUE;
		o_ptr->pval = 1;
	}

	/* Hack -- Jellies are immune to acid only if they are already acidic */
	if (strchr("j", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_ACID))
	{
		dam = damroll(8, 8);

		/* Total Immunity */
		if (!(p_ptr->immune_acid || (dam <= 0)))
		{
			/* Resist the damage */
			if (p_ptr->resist_acid) dam = (dam + 2) / 3;
			if (p_ptr->oppose_acid) dam = (dam + 2) / 3;

			/* Take damage */
			take_hit(dam, "acidic food");
		}
		harmful = TRUE;
	}

	/*
	 * Hack -- Jellies, kobolds, spiders, icky things, molds, and mushrooms
	 * are immune to poison because their body already contains
	 * poisonous chemicals.
	 */
	if (strchr("ijkmS,", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_POIS))
	{
		if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
		{
			set_poisoned(p_ptr->poisoned + rand_int(15) + 10);
		}
		harmful = TRUE;
	}

	/*
	 * Bad effects override good effects
	 * and hacked-up corpses lose intrinsics.
	 */
	if (!harmful && !cutting && (o_ptr->sval != SV_CORPSE_MEAT))
	{
		if (r_ptr->flags3 & RF3_IM_ACID)
		{
			set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10);
		}
		if (r_ptr->flags3 & RF3_IM_ELEC)
		{
			set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10);
		}
		if (r_ptr->flags3 & RF3_IM_FIRE)
		{
			set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10);
		}
		if (r_ptr->flags3 & RF3_IM_COLD)
		{
			set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10);
		}
		if (r_ptr->flags3 & RF3_IM_POIS)
		{
			set_oppose_pois(p_ptr->oppose_pois + rand_int(10) + 10);
		}
		if (r_ptr->flags3 & RF3_RES_NETH)
		{
			set_protevil(p_ptr->protevil + rand_int(25) + 3 * r_ptr->level);
		}
		if (r_ptr->flags3 & RF3_RES_PLAS)
		{
			set_oppose_fire(p_ptr->oppose_fire + rand_int(20) + 20);
		}
		if (r_ptr->flags2 & RF2_SHAPECHANGER)
		{
			// DGDGDG			(void)set_mimic(20 , rand_int(MIMIC_VALAR));
		}

		if (r_ptr->flags3 & RF3_DEMON)
		{
			// DGDGDG			(void)set_mimic(30 , MIMIC_DEMON);
		}

		if (r_ptr->flags3 & RF3_UNDEAD)
		{
			// DGDGDG			(void)set_mimic(30 , MIMIC_VAMPIRE);
		}

		if (r_ptr->flags3 & RF3_NO_FEAR)
		{
			(void)set_afraid(0);
		}
		if (r_ptr->flags3 & RF3_NO_STUN)
		{
			(void)set_stun(0);
		}
		if (r_ptr->flags3 & RF3_NO_CONF)
		{
			(void)set_confused(0);
		}
		if (r_ptr->flags6 & RF6_S_THUNDERLORD)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_THUNDERLORD, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_DEMON)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_KIN)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_KIN, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_HI_DEMON)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DEMON, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_MONSTER)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_MONSTERS)
		{
			int k;
			for (k = 0; k < 8; k++)
			{
				summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE);
			}
		}
		if (r_ptr->flags6 & RF6_S_UNDEAD)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_DRAGON)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_ANT)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_SPIDER)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_HOUND)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_HYDRA)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_ANGEL)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_HI_DRAGON)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_WRAITH)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH, FALSE);
		}
		if (r_ptr->flags6 & RF6_S_UNIQUE)
		{
			summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE, FALSE);
		}
	}
}


/*
 * Hook to determine if an object is eatable
 */
static bool_ item_tester_hook_eatable(object_type *o_ptr)
{
	/* Foods and, well, corpses are edible */
	if ((o_ptr->tval == TV_FOOD) || (o_ptr->tval == TV_CORPSE)) return (TRUE);

	/* Assume not */
	return (FALSE);
}


/*
 * Eat some food (from the pack or floor)
 */
void do_cmd_eat_food(void)
{
	int item, ident, lev, fval = 0;

	object_type *o_ptr;
	object_type *q_ptr, forge;

	monster_race *r_ptr;

	cptr q, s;

	bool_ destroy = TRUE;


	/* Restrict choices to food  */
	item_tester_hook = item_tester_hook_eatable;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Food full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Eat which item? ";
	s = "You have nothing to eat.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}

	/* Sound */
	sound(SOUND_EAT);


	/* Take a turn */
	energy_use = 100;

	/* Identity not known yet */
	ident = FALSE;

	/* Object level */
	lev = k_info[o_ptr->k_idx].level;

	/* Scripted foods */
	if (process_hooks_ret(HOOK_EAT, "d", "(O)", o_ptr))
	{
		ident = process_hooks_return[0].num;
	}
	/* (not quite) Normal foods */
	else if (o_ptr->tval == TV_FOOD)
	{
		/* Analyze the food */
		switch (o_ptr->sval)
		{
		case SV_FOOD_GREAT_HEALTH:
			{
				p_ptr->hp_mod += 70;
				msg_print("As you eat it you begin to feel your life flow getting stronger.");
				ident = TRUE;
				p_ptr->update |= (PU_HP);

				break;
			}

		case SV_FOOD_POISON:
			{
				if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
				{
					if (set_poisoned(p_ptr->poisoned + rand_int(10) + 10))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_BLINDNESS:
			{
				if (!p_ptr->resist_blind)
				{
					if (set_blind(p_ptr->blind + rand_int(200) + 200))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_PARANOIA:
			{
				if (!p_ptr->resist_fear)
				{
					if (set_afraid(p_ptr->afraid + rand_int(10) + 10))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_CONFUSION:
			{
				if (!p_ptr->resist_conf)
				{
					if (set_confused(p_ptr->confused + rand_int(10) + 10))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_HALLUCINATION:
			{
				if (!p_ptr->resist_chaos)
				{
					if (set_image(p_ptr->image + rand_int(250) + 250))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_PARALYSIS:
			{
				if (!p_ptr->free_act)
				{
					if (set_paralyzed(p_ptr->paralyzed + rand_int(10) + 10))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_FOOD_WEAKNESS:
			{
				take_hit(damroll(6, 6), "poisonous food");
				(void)do_dec_stat(A_STR, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_SICKNESS:
			{
				take_hit(damroll(6, 6), "poisonous food");
				(void)do_dec_stat(A_CON, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_STUPIDITY:
			{
				take_hit(damroll(8, 8), "poisonous food");
				(void)do_dec_stat(A_INT, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_NAIVETY:
			{
				take_hit(damroll(8, 8), "poisonous food");
				(void)do_dec_stat(A_WIS, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_UNHEALTH:
			{
				take_hit(damroll(10, 10), "poisonous food");
				(void)do_dec_stat(A_CON, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_DISEASE:
			{
				take_hit(damroll(10, 10), "poisonous food");
				(void)do_dec_stat(A_STR, STAT_DEC_NORMAL);

				ident = TRUE;

				break;
			}

		case SV_FOOD_CURE_POISON:
			{
				if (set_poisoned(0)) ident = TRUE;

				break;
			}

		case SV_FOOD_CURE_BLINDNESS:
			{
				if (set_blind(0)) ident = TRUE;

				break;
			}

		case SV_FOOD_CURE_PARANOIA:
			{
				if (set_afraid(0)) ident = TRUE;

				break;
			}

		case SV_FOOD_CURE_CONFUSION:
			{
				if (set_confused(0)) ident = TRUE;

				break;
			}

		case SV_FOOD_CURE_SERIOUS:
			{
				if (hp_player(damroll(4, 8))) ident = TRUE;

				break;
			}

		case SV_FOOD_RESTORE_STR:
			{
				if (do_res_stat(A_STR, TRUE)) ident = TRUE;

				break;
			}

		case SV_FOOD_RESTORE_CON:
			{
				if (do_res_stat(A_CON, TRUE)) ident = TRUE;

				break;
			}

		case SV_FOOD_RESTORING:
			{
				if (do_res_stat(A_STR, TRUE)) ident = TRUE;
				if (do_res_stat(A_INT, TRUE)) ident = TRUE;
				if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
				if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
				if (do_res_stat(A_CON, TRUE)) ident = TRUE;
				if (do_res_stat(A_CHR, TRUE)) ident = TRUE;

				break;
			}

		case SV_FOOD_FORTUNE_COOKIE:
			{
				char rumour[80];

				msg_print("That tastes good.");
				msg_print("There is message in the cookie. It says:");
				msg_print(NULL);

				switch (randint(20))
				{
				case 1:
					{
						get_rnd_line("chainswd.txt", rumour);
						break;
					}

				case 2:
					{
						get_rnd_line("error.txt", rumour);
						break;
					}

				case 3:
				case 4:
				case 5:
					{
						get_rnd_line("death.txt", rumour);
						break;
					}

				default:
					{
						get_rnd_line("rumors.txt", rumour);
						break;
					}
				}

				msg_format("%s", rumour);
				msg_print(NULL);

				ident = TRUE;

				break;
			}


		case SV_FOOD_RATION:
		case SV_FOOD_BISCUIT:
		case SV_FOOD_JERKY:
			{
				msg_print("That tastes good.");

				ident = TRUE;

				break;
			}

		case SV_FOOD_SLIME_MOLD:
			{
				msg_print("That tastes good.");

				/* 2% chance of getting the mold power */
				if (magik(2))
				{
					ADD_POWER(p_ptr->powers_mod, PWR_GROW_MOLD);
					p_ptr->update |= PU_POWERS;
				}

				ident = TRUE;

				break;
			}

		case SV_FOOD_WAYBREAD:
			{
				msg_print("That tastes very good.");
				(void)set_poisoned(0);
				(void)hp_player(damroll(4, 8));
				set_food(PY_FOOD_MAX - 1);

				ident = TRUE;

				break;
			}

		case SV_FOOD_PINT_OF_ALE:
		case SV_FOOD_PINT_OF_WINE:
			{
				msg_print("That tastes good.");

				ident = TRUE;

				q_ptr = &forge;
				object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
				q_ptr->number = 1;
				object_aware(q_ptr);
				object_known(q_ptr);
				q_ptr->ident |= IDENT_STOREB;
				(void)inven_carry(q_ptr, FALSE);

				break;
			}

		case SV_FOOD_ATHELAS:
			{
				msg_print("A fresh, clean essence rises, driving away wounds and poison.");

				(void)set_poisoned(0);
				(void)set_stun(0);
				(void)set_cut(0);
				if (p_ptr->black_breath)
				{
					msg_print("The hold of the Black Breath on you is broken!");
					p_ptr->black_breath = FALSE;
				}

				ident = TRUE;

				break;
			}
		}
	}

	/* Corpses... */
	else
	{
		r_ptr = &r_info[o_ptr->pval2];

		/* Analyse the corpse */
		switch (o_ptr->sval)
		{
		case SV_CORPSE_CORPSE:
			{
				bool_ no_meat = FALSE;

				/* Not all is edible. Apologies if messy. */

				/* Check weight -- they have to have some meat left */
				if (r_ptr->flags9 & RF9_DROP_SKELETON)
				{
					if (o_ptr->weight <= (r_ptr->weight * 3) / 5)
					{
						no_meat = TRUE;
					}
				}

				/* Non-skeletons are naturally have more allowances */
				else
				{
					if (o_ptr->weight <= (r_ptr->weight * 7) / 20)
					{
						no_meat = TRUE;
					}
				}

				/* Nothing left to eat */
				if (no_meat)
				{
					msg_print("There is not enough meat.");
					return;
				}


				/* Check freshness */
				if (!o_ptr->timeout) msg_print("Ugh! Raw meat!");
				else msg_print("That tastes good.");


				/* A pound of raw meat */
				o_ptr->pval -= 10;
				o_ptr->weight -= 10;

				/* Corpses still have meat on them */
				destroy = FALSE;

				ident = TRUE;

				break;
			}

		case SV_CORPSE_HEAD:
			{
				msg_print("You feel rather sick.");

				/* A pound of raw meat */
				o_ptr->pval -= 10;
				o_ptr->weight -= 10;

				/* Corpses still have meat on them */
				destroy = FALSE;

				ident = TRUE;

				break;
			}

		case SV_CORPSE_MEAT:
			{
				/* Just meat */
				if (!o_ptr->timeout) msg_print("You quickly swallow the meat.");
				else msg_print("That tastes good.");

				ident = TRUE;

				/* Those darn microorganisms */
				if (!o_ptr->timeout && (o_ptr->weight > o_ptr->pval) &&
				                !(p_ptr->resist_pois || p_ptr->oppose_pois))
				{
					set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
					             (o_ptr->weight - o_ptr->pval));
				}

				break;
			}
		}

		corpse_effect(o_ptr, FALSE);

		/* Less nutritious than food rations, but much more of it. */
		fval = (o_ptr->timeout) ? 2000 : 2500;

		/* Those darn microorganisms */
		if (!o_ptr->timeout && (o_ptr->weight - o_ptr->pval > 10) &&
		                !(p_ptr->resist_pois || p_ptr->oppose_pois))
		{
			set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) +
			             (o_ptr->weight - o_ptr->pval));
		}

		/* Partially cured */
		if (o_ptr->weight > o_ptr->timeout)
		{
			/* Adjust the "timeout" without overflowing */
			o_ptr->timeout = (o_ptr->timeout * ((100 * o_ptr->timeout) / o_ptr->weight)) / 100;
		}
	}


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

	/* We have tried it */
	object_tried(o_ptr);

	/* The player is now aware of the object */
	if (ident && !object_aware_p(o_ptr))
	{
		object_aware(o_ptr);
		gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
	}

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

	if (!fval) fval = o_ptr->pval;

	/* Food can feed the player, in a different ways */

	/* Vampires */
	if ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")))
	{
		/* Reduced nutritional benefit */
		/*		(void)set_food(p_ptr->food + (fval / 10)); -- No more */
		msg_print("Mere victuals hold scant sustenance for a being such as yourself.");

		/* Hungry */
		if (p_ptr->food < PY_FOOD_ALERT)
		{
			msg_print("Your hunger can only be satisfied with fresh blood!");
		}
	}

	else if (PRACE_FLAG(PR1_NO_FOOD))
	{
		if (PRACE_FLAG(PR1_UNDEAD))
		{
			msg_print("The food of mortals is poor sustenance for you.");
		}
		else
		{
			msg_print("Food is poor sustenance for you.");
		}
		set_food(p_ptr->food + ((fval) / 40));
	}

	/* Those living in fresh */
	else
	{
		(void)set_food(p_ptr->food + fval);
	}


	/* Destroy a food in the pack */
	if (destroy)
	{
		if (item >= 0)
		{
			inven_item_increase(item, -1);
			inven_item_describe(item);
			inven_item_optimize(item);
		}

		/* Destroy a food on the floor */
		else
		{
			floor_item_increase(0 - item, -1);
			floor_item_describe(0 - item);
			floor_item_optimize(0 - item);
		}
	}
}


/*
 * Cut a corpse up for convenient storage
 */
void do_cmd_cut_corpse(void)
{
	int item, meat = 0, not_meat = 0;

	object_type *o_ptr;

	object_type *i_ptr;

	object_type object_type_body;

	monster_race *r_ptr;

	cptr q, s;


	/* Restrict choices to corpses */
	item_tester_tval = TV_CORPSE;

	/* Get an item */
	q = "Hack up which corpse? ";
	s = "You have no corpses.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}

	r_ptr = &r_info[o_ptr->pval2];

	if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_HEAD))
	{
		msg_print ("You cannot split that.");
		return;
	}

	switch (o_ptr->sval)
	{
	case SV_CORPSE_CORPSE:
		{
			if (r_ptr->flags9 & RF9_DROP_SKELETON)
			{
				not_meat = (r_ptr->weight * 3) / 5;
			}
			else
			{
				not_meat = (r_ptr->weight * 7) / 20;
			}
			meat = r_ptr->weight + r_ptr->weight / 10 - not_meat;

			break;
		}

	case SV_CORPSE_HEAD:
		{
			not_meat = r_ptr->weight / 150;
			meat = r_ptr->weight / 30 + r_ptr->weight / 300 - not_meat;

			break;
		}
	}

	if ((o_ptr->weight <= not_meat) || (meat < 10))
	{
		msg_print("There is not enough meat.");
		return;
	}

	/* Hacking 10 pounds off */
	if (meat > 100) meat = 100;

	/* Take a turn */
	energy_use = 100;

	o_ptr->pval -= meat;
	o_ptr->weight -= meat;

	msg_print("You hack some meat off the corpse.");

	corpse_effect(o_ptr, TRUE);

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

	/* Make some meat */
	object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_MEAT));

	i_ptr->number = meat / 10;
	i_ptr->pval2 = o_ptr->pval2;

	/* Length of time before decay */
	i_ptr->pval = 1000 + rand_int(1000);

	if (inven_carry_okay(i_ptr))
	{
		inven_carry(i_ptr, TRUE);
	}
	else
	{
		drop_near(i_ptr, 0, p_ptr->py, p_ptr->px);
	}
}


/*
 * Use a potion to cure some meat
 *
 * Salt water works well.
 */
void do_cmd_cure_meat(void)
{
	int item, num, cure;

	object_type *o_ptr;

	object_type *i_ptr;

	cptr q, s;


	/* Restrict choices to corpses */
	item_tester_tval = TV_CORPSE;
	item_tester_hook = item_tester_hook_eatable;

	/* Get some meat */
	q = "Cure which meat? ";
	s = "You have no meat to cure.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}

	/* Restrict choices to potions */
	item_tester_tval = TV_POTION;

	/* Get a potion */
	q = "Use which potion? ";
	s = "You have no potions to use.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		i_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		i_ptr = &o_list[0 - item];
	}

	if (i_ptr->number > 1)
	{
		/* Get a number */
		get_count(1, i_ptr->number);

		/* Save it */
		num = command_arg;
	}
	else
	{
		num = 1;
	}

	if (num == 0) return;

	/* Take a turn */
	energy_use = 100;

	q = "You soak the meat.";
	s = "You soak the meat.";

	switch (i_ptr->sval)
	{
	case SV_POTION_SALT_WATER:
		{
			q = "You salt the meat.";
			cure = 200 * num;

			break;
		}

	case SV_POTION_POISON:
		{
			q = "You poison the meat.";
			cure = 0;
			o_ptr->pval /= 2;
			if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight;

			break;
		}

	case SV_POTION_CONFUSION:
		{
			cure = 80 * num;

			break;
		}

	case SV_POTION_SLOW_POISON:
		{
			cure = 20 * num;

			break;
		}

	case SV_POTION_CURE_POISON:
		{
			cure = 45 * num;

			break;
		}

	case SV_POTION_DEATH:
		{
			q = "You ruin the meat.";
			cure = 0;
			o_ptr->pval /= 10;
			if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2;

			break;
		}

	default:
		{
			cure = 0;

			break;
		}
	}

	/* Message */
	if (object_known_p(i_ptr)) msg_print(q);
	else msg_print(s);

	/* The meat is already spoiling */
	if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) ||
	                (o_ptr->weight - o_ptr->pval > 10))
	{
		cure = (cure * o_ptr->pval) / (o_ptr->weight * 20);
	}

	/* Cure the meat */
	o_ptr->timeout += cure / o_ptr->number;

	if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval;

	/* Use up the potions in the pack */
	if (item >= 0)
	{
		inven_item_increase(item, 0 - num);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Use up the potions on the floor */
	else
	{
		floor_item_increase(0 - item, 0 - num);
		floor_item_describe(0 - item);
		floor_item_optimize(0 - item);
	}
}


/*
 * Hook to determine if an object is quaffable
 */
static bool_ item_tester_hook_quaffable(object_type *o_ptr)
{
	if ((o_ptr->tval == TV_POTION) || (o_ptr->tval == TV_POTION2)) return (TRUE);

	/* Assume not */
	return (FALSE);
}


static bool_ quaff_potion(int tval, int sval, int pval, int pval2)
{
	int ident = FALSE;


	/* "Traditional" potions */
	if (tval == TV_POTION)
	{
		switch (sval)
		{
		case SV_POTION_WATER:
		case SV_POTION_APPLE_JUICE:
		case SV_POTION_SLIME_MOLD:
			{
				msg_print("You feel less thirsty.");
				ident = TRUE;

				break;
			}

		case SV_POTION_SLOWNESS:
			{
				if (set_slow(p_ptr->slow + randint(25) + 15)) ident = TRUE;

				break;
			}

		case SV_POTION_SALT_WATER:
			{
				msg_print("The potion makes you vomit!");
				(void)set_food(PY_FOOD_STARVE - 1);
				(void)set_poisoned(0);
				(void)set_paralyzed(p_ptr->paralyzed + 4);
				ident = TRUE;

				break;
			}

		case SV_POTION_POISON:
			{
				if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
				{
					if (set_poisoned(p_ptr->poisoned + rand_int(15) + 10))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_POTION_BLINDNESS:
			{
				if (!p_ptr->resist_blind)
				{
					if (set_blind(p_ptr->blind + rand_int(100) + 100))
					{
						ident = TRUE;
					}
				}

				break;
			}

			/* Booze */
		case SV_POTION_CONFUSION:
			{
				if (!((p_ptr->resist_conf) || (p_ptr->resist_chaos)))
				{
					if (set_confused(p_ptr->confused + rand_int(20) + 15))
					{
						ident = TRUE;
					}
					if (randint(2) == 1)
					{
						if (set_image(p_ptr->image + rand_int(150) + 150))
						{
							ident = TRUE;
						}
					}
					if (randint(13) == 1)
					{
						ident = TRUE;
						if (randint(3) == 1) lose_all_info();
						else wiz_dark();
						teleport_player(100);
						wiz_dark();
						msg_print("You wake up elsewhere with a sore head...");
						msg_print("You can't remember a thing, or how you got here!");
					}
				}

				break;
			}

		case SV_POTION_SLEEP:
			{
				if (!p_ptr->free_act)
				{
					if (set_paralyzed(p_ptr->paralyzed + rand_int(4) + 4))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_POTION_LOSE_MEMORIES:
			{
				if (!p_ptr->hold_life && (p_ptr->exp > 0))
				{
					msg_print("You feel your memories fade.");
					lose_exp(p_ptr->exp / 4);
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_RUINATION:
			{
				msg_print("Your nerves and muscles feel weak and lifeless!");
				take_hit(damroll(10, 10), "a potion of Ruination");
				(void)dec_stat(A_DEX, 25, TRUE);
				(void)dec_stat(A_WIS, 25, TRUE);
				(void)dec_stat(A_CON, 25, TRUE);
				(void)dec_stat(A_STR, 25, TRUE);
				(void)dec_stat(A_CHR, 25, TRUE);
				(void)dec_stat(A_INT, 25, TRUE);
				ident = TRUE;

				break;
			}

		case SV_POTION_DEC_STR:
			{
				if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DEC_INT:
			{
				if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DEC_WIS:
			{
				if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DEC_DEX:
			{
				if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DEC_CON:
			{
				if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DEC_CHR:
			{
				if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) ident = TRUE;

				break;
			}

		case SV_POTION_DETONATIONS:
			{
				msg_print("Massive explosions rupture your body!");
				take_hit(damroll(50, 20), "a potion of Detonation");
				(void)set_stun(p_ptr->stun + 75);
				(void)set_cut(p_ptr->cut + 5000);
				ident = TRUE;

				break;
			}

		case SV_POTION_DEATH:
			{
				msg_print("A feeling of Death flows through your body.");
				take_hit(5000, "a potion of Death");
				ident = TRUE;

				break;
			}

		case SV_POTION_INFRAVISION:
			{
				if (set_tim_infra(p_ptr->tim_infra + 100 + randint(100)))
				{
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_DETECT_INVIS:
			{
				if (set_tim_invis(p_ptr->tim_invis + 12 + randint(12)))
				{
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_SLOW_POISON:
			{
				if (set_poisoned(p_ptr->poisoned / 2)) ident = TRUE;

				break;
			}

		case SV_POTION_CURE_POISON:
			{
				if (set_poisoned(0)) ident = TRUE;

				break;
			}

		case SV_POTION_BOLDNESS:
			{
				if (set_afraid(0)) ident = TRUE;

				break;
			}

		case SV_POTION_SPEED:
			{
				if (!p_ptr->fast)
				{
					if (set_fast(randint(25) + 15, 10)) ident = TRUE;
				}
				else
				{
					(void)set_fast(p_ptr->fast + 5, 10);
				}

				break;
			}

		case SV_POTION_RESIST_HEAT:
			{
				if (set_oppose_fire(p_ptr->oppose_fire + randint(10) + 10))
				{
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_RESIST_COLD:
			{
				if (set_oppose_cold(p_ptr->oppose_cold + randint(10) + 10))
				{
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_HEROISM:
			{
				if (set_afraid(0)) ident = TRUE;
				if (set_hero(p_ptr->hero + randint(25) + 25)) ident = TRUE;
				if (hp_player(10)) ident = TRUE;

				break;
			}

		case SV_POTION_BESERK_STRENGTH:
			{
				if (set_afraid(0)) ident = TRUE;
				if (set_shero(p_ptr->shero + randint(25) + 25)) ident = TRUE;
				if (hp_player(30)) ident = TRUE;

				break;
			}

		case SV_POTION_CURE_LIGHT:
			{
				if (hp_player(damroll(2, 8))) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_cut(p_ptr->cut - 10)) ident = TRUE;

				break;
			}

		case SV_POTION_CURE_SERIOUS:
			{
				if (hp_player(damroll(4, 8))) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_confused(0)) ident = TRUE;
				if (set_cut((p_ptr->cut / 2) - 50)) ident = TRUE;

				break;
			}

		case SV_POTION_CURE_CRITICAL:
			{
				if (hp_player(damroll(6, 8))) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_confused(0)) ident = TRUE;
				if (set_poisoned(0)) ident = TRUE;
				if (set_stun(0)) ident = TRUE;
				if (set_cut(0)) ident = TRUE;

				break;
			}

		case SV_POTION_HEALING:
			{
				if (hp_player(300)) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_confused(0)) ident = TRUE;
				if (set_poisoned(0)) ident = TRUE;
				if (set_stun(0)) ident = TRUE;
				if (set_cut(0)) ident = TRUE;

				break;
			}

		case SV_POTION_STAR_HEALING:
			{
				if (hp_player(1200)) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_confused(0)) ident = TRUE;
				if (set_poisoned(0)) ident = TRUE;
				if (set_stun(0)) ident = TRUE;
				if (set_cut(0)) ident = TRUE;

				break;
			}

		case SV_POTION_LIFE:
			{
				msg_print("You feel life flow through your body!");
				restore_level();
				hp_player(5000);
				(void)set_poisoned(0);
				(void)set_blind(0);
				(void)set_confused(0);
				(void)set_image(0);
				(void)set_stun(0);
				(void)set_cut(0);
				(void)do_res_stat(A_STR, TRUE);
				(void)do_res_stat(A_CON, TRUE);
				(void)do_res_stat(A_DEX, TRUE);
				(void)do_res_stat(A_WIS, TRUE);
				(void)do_res_stat(A_INT, TRUE);
				(void)do_res_stat(A_CHR, TRUE);
				if (p_ptr->black_breath)
				{
					msg_print("The hold of the Black Breath on you is broken!");
				}
				p_ptr->black_breath = FALSE;
				ident = TRUE;

				break;
			}

		case SV_POTION_RESTORE_MANA:
			{
				if (p_ptr->csp < p_ptr->msp)
				{
					p_ptr->csp = p_ptr->msp;
					p_ptr->csp_frac = 0;
					msg_print("Your feel your head clear.");
					p_ptr->redraw |= (PR_MANA);
					p_ptr->window |= (PW_PLAYER);
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_RESTORE_EXP:
			{
				if (restore_level()) ident = TRUE;

				break;
			}

		case SV_POTION_RES_STR:
			{
				if (do_res_stat(A_STR, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_RES_INT:
			{
				if (do_res_stat(A_INT, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_RES_WIS:
			{
				if (do_res_stat(A_WIS, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_RES_DEX:
			{
				if (do_res_stat(A_DEX, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_RES_CON:
			{
				if (do_res_stat(A_CON, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_RES_CHR:
			{
				if (do_res_stat(A_CHR, TRUE)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_STR:
			{
				if (do_inc_stat(A_STR)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_INT:
			{
				if (do_inc_stat(A_INT)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_WIS:
			{
				if (do_inc_stat(A_WIS)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_DEX:
			{
				if (do_inc_stat(A_DEX)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_CON:
			{
				if (do_inc_stat(A_CON)) ident = TRUE;

				break;
			}

		case SV_POTION_INC_CHR:
			{
				if (do_inc_stat(A_CHR)) ident = TRUE;

				break;
			}

		case SV_POTION_AUGMENTATION:
			{
				if (do_inc_stat(A_STR)) ident = TRUE;
				if (do_inc_stat(A_INT)) ident = TRUE;
				if (do_inc_stat(A_WIS)) ident = TRUE;
				if (do_inc_stat(A_DEX)) ident = TRUE;
				if (do_inc_stat(A_CON)) ident = TRUE;
				if (do_inc_stat(A_CHR)) ident = TRUE;

				break;
			}

		case SV_POTION_ENLIGHTENMENT:
			{
				msg_print("An image of your surroundings forms in your mind...");
				wiz_lite();
				ident = TRUE;

				break;
			}

		case SV_POTION_STAR_ENLIGHTENMENT:
			{
				msg_print("You begin to feel more enlightened...");
				msg_print(NULL);
				wiz_lite_extra();
				(void)do_inc_stat(A_INT);
				(void)do_inc_stat(A_WIS);
				(void)detect_traps(DEFAULT_RADIUS);
				(void)detect_doors(DEFAULT_RADIUS);
				(void)detect_stairs(DEFAULT_RADIUS);
				(void)detect_treasure(DEFAULT_RADIUS);
				(void)detect_objects_gold(DEFAULT_RADIUS);
				(void)detect_objects_normal(DEFAULT_RADIUS);
				identify_pack();
				self_knowledge(NULL);
				ident = TRUE;

				break;
			}

		case SV_POTION_SELF_KNOWLEDGE:
			{
				msg_print("You begin to know yourself a little better...");
				msg_print(NULL);
				self_knowledge(NULL);
				ident = TRUE;

				break;
			}

		case SV_POTION_EXPERIENCE:
			{
				if (p_ptr->exp < PY_MAX_EXP)
				{
					s32b ee = (p_ptr->exp / 2) + 10;
					if (ee > 100000L) ee = 100000L;
					msg_print("You feel more experienced.");
					gain_exp(ee);
					ident = TRUE;
				}

				break;
			}

		case SV_POTION_RESISTANCE:
			{
				(void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
				(void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
				(void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
				(void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
				(void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
				ident = TRUE;

				break;
			}

		case SV_POTION_CURING:
			{
				if (hp_player(50)) ident = TRUE;
				if (set_blind(0)) ident = TRUE;
				if (set_poisoned(0)) ident = TRUE;
				if (set_confused(0)) ident = TRUE;
				if (set_stun(0)) ident = TRUE;
				if (set_cut(0)) ident = TRUE;
				if (set_image(0)) ident = TRUE;
				if (heal_insanity(50)) ident = TRUE;

				break;
			}

		case SV_POTION_INVULNERABILITY:
			{
				(void)set_invuln(p_ptr->invuln + randint(7) + 7);
				ident = TRUE;

				break;
			}

		case SV_POTION_NEW_LIFE:
			{
				do_cmd_rerate();
				ident = TRUE;

				break;
			}

		case SV_POTION_BLOOD:
			{
				msg_print("You feel the blood of life running through your veins!");
				ident = TRUE;
				p_ptr->allow_one_death++;

				break;
			}

		case SV_POTION_MUTATION:
			{
				msg_print("You feel the dark corruptions of Morgoth coming over you!");
				gain_random_corruption(0);
				ident = TRUE;
				break;
			}

		case SV_POTION_INVIS:
			{
				int t = 30 + randint(30);

				if (set_invis(p_ptr->tim_invis + t, 35))
				{
					ident = TRUE;
				}
				set_tim_invis(p_ptr->tim_invis + t);

				break;
			}

		case SV_POTION_LEARNING:
			{
				p_ptr->skill_points += rand_range(4, 10 + luck( -4, 4));
				cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);

				break;
			}

		default:
			{
				break;
			}
		}
	}

	/* "Duplicate" potions */
	else
	{
		switch (sval)
		{
		case SV_POTION2_MIMIC:
			{
				if (!p_ptr->mimic_form)
				{
					s32b time;

					call_lua("get_mimic_rand_dur", "(d)", "d", pval2, &time);

					set_mimic(time, pval2, (p_ptr->lev * 2) / 3);

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

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

					ident = TRUE;
				}

				break;
			}

		case SV_POTION2_CURE_LIGHT_SANITY:
			{
				if (heal_insanity(damroll(4, 8))) ident = TRUE;

				break;
			}

		case SV_POTION2_CURE_SERIOUS_SANITY:
			{
				if (heal_insanity(damroll(8, 8))) ident = TRUE;

				break;
			}

		case SV_POTION2_CURE_CRITICAL_SANITY:
			{
				if (heal_insanity(damroll(12, 8))) ident = TRUE;

				break;
			}

		case SV_POTION2_CURE_SANITY:
			{
				if (heal_insanity(damroll(10, 100))) ident = TRUE;

				break;
			}

		default:
			{
				break;
			}
		}
	}

	return (ident);
}


/*
 * Quaff a potion (from the pack or the floor)
 */
void do_cmd_quaff_potion(void)
{
	int item, ident, lev;

	object_type *o_ptr;

	object_type *q_ptr, forge;

	cptr q, s;


	/* Restrict choices to potions */
	item_tester_hook = item_tester_hook_quaffable;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Potion full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Quaff which potion? ";
	s = "You have no potions to quaff.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}


	/* Sound */
	sound(SOUND_QUAFF);


	/* Take a turn */
	energy_use = 100;

	/* Not identified yet */
	ident = FALSE;

	/* Object level */
	lev = k_info[o_ptr->k_idx].level;

	/* Analyze the potion */
	if (process_hooks_ret(HOOK_QUAFF, "d", "(O)", o_ptr))
	{
		ident = process_hooks_return[0].num;
	}
	else
	{
		ident = quaff_potion(o_ptr->tval, o_ptr->sval, o_ptr->pval, o_ptr->pval2);
	}

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

	/* The item has been tried */
	object_tried(o_ptr);

	/* An identification was made */
	if (ident && !object_aware_p(o_ptr))
	{
		object_aware(o_ptr);
		gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
	}

	if (get_skill(SKILL_ALCHEMY))
	{
		if (item >= 0)
		{
			q_ptr = &forge;
			object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1));
			q_ptr->number = 1;
			object_aware(q_ptr);
			object_known(q_ptr);

			q_ptr->ident |= IDENT_STOREB;

			(void)inven_carry(q_ptr, FALSE);
		}
	}

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


	/* Potions can feed the player */
	(void)set_food(p_ptr->food + o_ptr->pval);


	/* Destroy a potion in the pack */
	if (item >= 0)
	{
		inven_item_increase(item, -1);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Destroy a potion on the floor */
	else
	{
		floor_item_increase(0 - item, -1);
		floor_item_describe(0 - item);
		floor_item_optimize(0 - item);
	}
}


/*
 * Drink from a fountain
 */
void do_cmd_drink_fountain(void)
{
	cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];

	bool_ ident;

	int tval, sval, pval = 0;

	int i;

	char ch;


	/* Is the fountain empty? */
	if (c_ptr->special2 <= 0)
	{
		msg_print("The fountain is dried out.");
		return;
	}

	/* We quaff or we fill ? */
	if (!get_com("Do you want to [Q]uaff or [F]ill from the fountain? ", &ch))
	{
		return;
	}

	if ((ch == 'F') || (ch == 'f'))
	{
		do_cmd_fill_bottle();

		return;
	}

	else if ((ch == 'Q') || (ch == 'q'))
	{
		if (c_ptr->special <= SV_POTION_LAST)
		{
			tval = TV_POTION;
			sval = c_ptr->special;
		}
		else
		{
			tval = TV_POTION2;
			sval = c_ptr->special - SV_POTION_LAST;
		}

		for (i = 0; i < max_k_idx; i++)
		{
			object_kind *k_ptr = &k_info[i];

			if (k_ptr->tval != tval) continue;
			if (k_ptr->sval != sval) continue;

			pval = k_ptr->pval;

			break;
		}

		ident = quaff_potion(tval, sval, pval, 0);

		c_ptr->special2--;

		if (c_ptr->special2 <= 0)
		{
			cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
		}

		if (ident) c_ptr->info |= CAVE_IDNT;
	}
}


/*
 * Fill an empty bottle
 */
void do_cmd_fill_bottle(void)
{
	cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px];

	int tval, sval, item, amt = 1;

	object_type *q_ptr, *o_ptr, forge;

	cptr q, s;

	/* Is the fountain empty? */
	/*
	 * This check is redundant as it is done in do_cmd_drink_fountain()
	 * but I keep this because someone might want to call this directly.
	 * -- Kusunose
	 */
	if (c_ptr->special2 <= 0)
	{
		msg_print("The fountain has dried up.");
		return;
	}

	/* Determine the tval/sval of the potion */
	if (c_ptr->special <= SV_POTION_LAST)
	{
		tval = TV_POTION;
		sval = c_ptr->special;
	}
	else
	{
		tval = TV_POTION2;
		sval = c_ptr->special - SV_POTION_LAST;
	}

	/* Restrict choices to bottles */
	item_tester_tval = TV_BOTTLE;

	/* Get an item */
	q = "Fill which bottle? ";
	s = "You have no bottles to fill.";
	if (!get_item(&item, q, s, (USE_INVEN))) return;
	o_ptr = &p_ptr->inventory[item];

	/* Find out how many the player wants */
	if (o_ptr->number > 1)
	{
		/* Get a quantity */
		amt = get_quantity(NULL, o_ptr->number);

		/* Allow user abort */
		if (amt <= 0) return;
	}

	if (amt > c_ptr->special2) amt = c_ptr->special2;

	/* Destroy bottles in the pack */
	if (item >= 0)
	{
		inven_item_increase(item, -amt);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Destroy bottles on the floor */
	else
	{
		floor_item_increase(0 - item, -amt);
		floor_item_describe(0 - item);
		floor_item_optimize(0 - item);
	}

	/* Create the potion */
	q_ptr = &forge;
	object_prep(q_ptr, lookup_kind(tval, sval));
	q_ptr->number = amt;

	if (c_ptr->info & CAVE_IDNT)
	{
		object_aware(q_ptr);
		object_known(q_ptr);
	}

	inven_carry(q_ptr, TRUE);

	c_ptr->special2 -= amt;

	if (c_ptr->special2 <= 0)
	{
		cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
	}

	return;
}


/*
 * Curse the players armor
 */
bool_ curse_armor(void)
{
	object_type *o_ptr;

	char o_name[80];


	/* Curse the body armor */
	o_ptr = &p_ptr->inventory[INVEN_BODY];

	/* Nothing to curse */
	if (!o_ptr->k_idx) return (FALSE);


	/* Describe */
	object_desc(o_name, o_ptr, FALSE, 3);

	/* Attempt a saving throw for artifacts */
	if (((o_ptr->art_name) || artifact_p(o_ptr)) && (rand_int(100) < 50))
	{
		/* Cool */
		msg_format("A terrible black aura tries to surround your armour, "
		           "but your %s resists the effects!", o_name);
	}

	/* not artifact or failed save... */
	else
	{
		/* Oops */
		msg_format("A terrible black aura blasts your %s!", o_name);

		/* Blast the armor */
		o_ptr->name1 = 0;
		o_ptr->name2 = EGO_BLASTED;
		o_ptr->to_a = 0 - randint(5) - randint(5);
		o_ptr->to_h = 0;
		o_ptr->to_d = 0;
		o_ptr->ac = 0;
		o_ptr->dd = 0;
		o_ptr->ds = 0;
		o_ptr->art_flags1 = 0;
		o_ptr->art_flags2 = 0;
		o_ptr->art_flags3 = 0;
		o_ptr->art_flags4 = 0;

		/* Curse it */
		o_ptr->ident |= (IDENT_CURSED);

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

		/* Recalculate mana */
		p_ptr->update |= (PU_MANA);

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

	return (TRUE);
}


/*
 * Curse the players weapon
 */
bool_ curse_weapon(void)
{
	object_type *o_ptr;

	char o_name[80];


	/* Curse the weapon */
	o_ptr = &p_ptr->inventory[INVEN_WIELD];

	/* Nothing to curse */
	if (!o_ptr->k_idx) return (FALSE);


	/* Describe */
	object_desc(o_name, o_ptr, FALSE, 3);

	/* Attempt a saving throw */
	if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 50))
	{
		/* Cool */
		msg_format("A terrible black aura tries to surround your weapon, "
		           "but your %s resists the effects!", o_name);
	}

	/* not artifact or failed save... */
	else
	{
		/* Oops */
		msg_format("A terrible black aura blasts your %s!", o_name);

		/* Shatter the weapon */
		o_ptr->name1 = 0;
		o_ptr->name2 = EGO_SHATTERED;
		o_ptr->to_h = 0 - randint(5) - randint(5);
		o_ptr->to_d = 0 - randint(5) - randint(5);
		o_ptr->to_a = 0;
		o_ptr->ac = 0;
		o_ptr->dd = 0;
		o_ptr->ds = 0;
		o_ptr->art_flags1 = 0;
		o_ptr->art_flags2 = 0;
		o_ptr->art_flags3 = 0;
		o_ptr->art_flags4 = 0;


		/* Curse it */
		o_ptr->ident |= (IDENT_CURSED);

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

		/* Recalculate mana */
		p_ptr->update |= (PU_MANA);

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

	/* Notice */
	return (TRUE);
}


/*
 * Hook to determine if an object is readable
 */
static bool_ item_tester_hook_readable(object_type *o_ptr)
{
	if ((o_ptr->tval == TV_SCROLL) || (o_ptr->tval == TV_PARCHMENT)) return (TRUE);

	/* Assume not */
	return (FALSE);
}


/*
 * Read a scroll (from the pack or floor).
 *
 * Certain scrolls can be "aborted" without losing the scroll.  These
 * include scrolls with no effects but recharge or identify, which are
 * cancelled before use.  XXX Reading them still takes a turn, though.
 */
void do_cmd_read_scroll(void)
{
	int item, k, used_up, ident, lev;

	object_type *o_ptr;

	object_type *q_ptr, forge;

	cptr q, s;


	/* Check some conditions */
	if (p_ptr->blind)
	{
		msg_print("You can't see anything.");
		return;
	}

	if (no_lite())
	{
		msg_print("You have no light by which to read.");
		return;
	}

	if (p_ptr->confused)
	{
		msg_print("You are too confused!");
		return;
	}


	/* Restrict choices to scrolls */
	item_tester_hook = item_tester_hook_readable;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Scroll full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Read which scroll? ";
	s = "You have no scrolls to read.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}


	/* Take a turn */
	energy_use = 100;

	/* Not identified yet */
	ident = FALSE;

	/* Object level */
	lev = k_info[o_ptr->k_idx].level;

	/* Assume the scroll will get used up */
	used_up = TRUE;

	/* New scripts, can override the ingame code */
	if (process_hooks_ret(HOOK_READ, "dd", "(O)", o_ptr))
	{
		used_up = process_hooks_return[0].num;
		ident = process_hooks_return[1].num;
	}
	/* Traditional scrolls */
	else if (o_ptr->tval == TV_SCROLL)
	{
		/* Analyze the scroll */
		switch (o_ptr->sval)
		{
		case SV_SCROLL_MASS_RESURECTION:
			{
				int k;

				ident = TRUE;
				msg_print("You feel the souls of the dead coming back "
				          "from the Halls of Mandos.");

				for (k = 0; k < max_r_idx; k++)
				{
					monster_race *r_ptr = &r_info[k];

					if (r_ptr->flags1 & RF1_UNIQUE &&
					                !(r_ptr->flags9 & RF9_SPECIAL_GENE))
					{
						r_ptr->max_num = 1;
					}
				}

				break;
			}

		case SV_SCROLL_DEINCARNATION:
			{
				if (!get_check("Do you really want to leave your body? "
				                "(beware, it'll be destroyed!) "))
				{
					used_up = FALSE;
					break;
				}

				do_cmd_leave_body(FALSE);

				ident = TRUE;
				used_up = TRUE;

				break;
			}

			/* original didn't set used_up flag ??? -- pelpel */
		case SV_SCROLL_RESET_RECALL:
			{
				if (!reset_recall(TRUE))
				{
					used_up = FALSE;
					break;
				}

				msg_format("Recall reset to %s at level %d.",
				           d_info[p_ptr->recall_dungeon].name + d_name,
				           max_dlv[p_ptr->recall_dungeon]);

				ident = TRUE;
				used_up = TRUE;

				break;
			}

		case SV_SCROLL_DIVINATION:
			{
				int i, count = 0;
				char buf[120];

				while (count < 1000)
				{
					count++;
					i = rand_int(MAX_FATES);
					if (!fates[i].fate) continue;
					if (fates[i].know) continue;

					msg_print("A message appears on the scroll. It says:");
					msg_print(NULL);

					fate_desc(buf, i);
					msg_format("%s", buf);

					msg_print(NULL);
					msg_print("The scroll disappears in a puff of smoke!");

					fates[i].know = TRUE;
					ident = TRUE;

					break;
				}

				break;
			}

		case SV_SCROLL_DARKNESS:
			{
				if (!(p_ptr->resist_blind) && !(p_ptr->resist_dark))
				{
					(void)set_blind(p_ptr->blind + 3 + randint(5));
				}
				if (unlite_area(10, 3)) ident = TRUE;

				break;
			}

		case SV_SCROLL_AGGRAVATE_MONSTER:
			{
				msg_print("There is a high-pitched humming noise.");
				aggravate_monsters(1);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_CURSE_ARMOR:
			{
				if (curse_armor()) ident = TRUE;

				break;
			}

		case SV_SCROLL_CURSE_WEAPON:
			{
				if (curse_weapon()) ident = TRUE;

				break;
			}

		case SV_SCROLL_SUMMON_MONSTER:
			{
				for (k = 0; k < randint(3); k++)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, dun_level, 0))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_SCROLL_SUMMON_MINE:
			{
				if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE))
				{
					ident = TRUE;
				}

				break;
			}

		case SV_SCROLL_SUMMON_UNDEAD:
			{
				for (k = 0; k < randint(3); k++)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD))
					{
						ident = TRUE;
					}
				}

				break;
			}

		case SV_SCROLL_TRAP_CREATION:
			{
				if (trap_creation()) ident = TRUE;

				break;
			}

		case SV_SCROLL_PHASE_DOOR:
			{
				teleport_player(10);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_TELEPORT:
			{
				teleport_player(100);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_TELEPORT_LEVEL:
			{
				(void)teleport_player_level();

				ident = TRUE;

				break;
			}

		case SV_SCROLL_WORD_OF_RECALL:
			{
				if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
				{
					used_up = FALSE;
				}
				else
				{
					recall_player(21, 15);

					ident = TRUE;
				}

				break;
			}

		case SV_SCROLL_IDENTIFY:
			{
				ident = TRUE;

				if (!ident_spell()) used_up = FALSE;

				break;
			}

		case SV_SCROLL_STAR_IDENTIFY:
			{
				ident = TRUE;

				if (!identify_fully()) used_up = FALSE;

				break;
			}

		case SV_SCROLL_REMOVE_CURSE:
			{
				if (remove_curse())
				{
					msg_print("You feel as if someone is watching over you.");
					ident = TRUE;
				}

				break;
			}

		case SV_SCROLL_STAR_REMOVE_CURSE:
			{
				remove_all_curse();

				ident = TRUE;

				break;
			}

		case SV_SCROLL_ENCHANT_ARMOR:
			{
				ident = TRUE;

				if (!enchant_spell(0, 0, 1, 0)) used_up = FALSE;

				break;
			}

		case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
			{
				if (!enchant_spell(1, 0, 0, 0)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
			{
				if (!enchant_spell(0, 1, 0, 0)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_ENCHANT_WEAPON_PVAL:
			{
				if (!enchant_spell(0, 0, 0, 1)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_STAR_ENCHANT_ARMOR:
			{
				if (!enchant_spell(0, 0, randint(3) + 2, 0)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_STAR_ENCHANT_WEAPON:
			{
				if (!enchant_spell(randint(3), randint(3), 0, 0)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_RECHARGING:
			{
				if (!recharge(60)) used_up = FALSE;

				ident = TRUE;

				break;
			}

		case SV_SCROLL_LIGHT:
			{
				if (lite_area(damroll(2, 8), 2)) ident = TRUE;

				break;
			}

		case SV_SCROLL_MAPPING:
			{
				map_area();

				ident = TRUE;

				break;
			}

		case SV_SCROLL_DETECT_GOLD:
			{
				if (detect_treasure(DEFAULT_RADIUS)) ident = TRUE;
				if (detect_objects_gold(DEFAULT_RADIUS)) ident = TRUE;

				break;
			}

		case SV_SCROLL_DETECT_ITEM:
			{
				if (detect_objects_normal(DEFAULT_RADIUS)) ident = TRUE;

				break;
			}

		case SV_SCROLL_DETECT_TRAP:
			{
				if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;

				break;
			}

		case SV_SCROLL_DETECT_DOOR:
			{
				if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
				if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;

				break;
			}

		case SV_SCROLL_DETECT_INVIS:
			{
				if (detect_monsters_invis(DEFAULT_RADIUS)) ident = TRUE;

				break;
			}

		case SV_SCROLL_SATISFY_HUNGER:
			{
				if (set_food(PY_FOOD_MAX - 1)) ident = TRUE;

				break;
			}

		case SV_SCROLL_BLESSING:
			{
				if (set_blessed(p_ptr->blessed + randint(12) + 6)) ident = TRUE;

				break;
			}

		case SV_SCROLL_HOLY_CHANT:
			{
				if (set_blessed(p_ptr->blessed + randint(24) + 12)) ident = TRUE;

				break;
			}

		case SV_SCROLL_HOLY_PRAYER:
			{
				if (set_blessed(p_ptr->blessed + randint(48) + 24)) ident = TRUE;

				break;
			}

		case SV_SCROLL_MONSTER_CONFUSION:
			{
				if (p_ptr->confusing == 0)
				{
					msg_print("Your hands begin to glow.");
					p_ptr->confusing = TRUE;
					ident = TRUE;
				}

				break;
			}

		case SV_SCROLL_PROTECTION_FROM_EVIL:
			{
				k = 3 * p_ptr->lev;
				if (set_protevil(p_ptr->protevil + randint(25) + k)) ident = TRUE;

				break;
			}

		case SV_SCROLL_RUNE_OF_PROTECTION:
			{
				warding_glyph();

				ident = TRUE;

				break;
			}

		case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
			{
				if (destroy_doors_touch()) ident = TRUE;

				break;
			}

		case SV_SCROLL_STAR_DESTRUCTION:
			{
				/* Prevent destruction of quest levels and town */
				if (!is_quest(dun_level) && dun_level)
				{
					destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);
				}
				else
				{
					msg_print("The dungeon trembles...");
				}

				ident = TRUE;

				break;
			}

		case SV_SCROLL_DISPEL_UNDEAD:
			{
				if (dispel_undead(60)) ident = TRUE;

				break;
			}

		case SV_SCROLL_GENOCIDE:
			{
				(void)genocide(TRUE);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_MASS_GENOCIDE:
			{
				(void)mass_genocide(TRUE);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_ACQUIREMENT:
			{
				acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE);

				ident = TRUE;

				break;
			}

		case SV_SCROLL_STAR_ACQUIREMENT:
			{
				acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE);

				ident = TRUE;

				break;
			}

			/* ZAngband scrolls */
		case SV_SCROLL_FIRE:
			{
				fire_ball(GF_FIRE, 0, 150, 4);

				/*
				 * Note: "Double" damage since it is centered on
				 * the player ...
				 */
				if (!p_ptr->oppose_fire && !p_ptr->resist_fire &&
				                !p_ptr->immune_fire)
				{
					take_hit(50 + randint(50) + (p_ptr->sensible_fire) ? 20 : 0,
					         "a Scroll of Fire");
				}

				ident = TRUE;

				break;
			}


		case SV_SCROLL_ICE:
			{
				fire_ball(GF_ICE, 0, 175, 4);

				if (!p_ptr->oppose_cold && !p_ptr->resist_cold &&
				                !p_ptr->immune_cold)
				{
					take_hit(100 + randint(100), "a Scroll of Ice");
				}

				ident = TRUE;

				break;
			}

		case SV_SCROLL_CHAOS:
			{
				fire_ball(GF_CHAOS, 0, 222, 4);

				if (!p_ptr->resist_chaos)
				{
					take_hit(111 + randint(111), "a Scroll of Chaos");
				}

				ident = TRUE;

				break;
			}

		case SV_SCROLL_RUMOR:
			{
				char rumour[80];

				msg_print("There is message on the scroll. It says:");
				msg_print(NULL);

				/* Pick random text */
				switch (randint(20))
				{
				case 1:
					{
						get_rnd_line("chainswd.txt", rumour);

						break;
					}

				case 2:
					{
						get_rnd_line("error.txt", rumour);

						break;
					}

				case 3:
				case 4:
				case 5:
					{
						get_rnd_line("death.txt", rumour);

						break;
					}

				default:
					{
						get_rnd_line("rumors.txt", rumour);

						break;
					}
				}

				msg_format("%s", rumour);
				msg_print(NULL);

				msg_print("The scroll disappears in a puff of smoke!");

				ident = TRUE;

				break;
			}

		case SV_SCROLL_ARTIFACT:
			{
				ident = TRUE;

				if (!artifact_scroll()) used_up = FALSE;

				break;
			}

		default:
			{
				break;
			}
		}
	}

	/* Other readable items */
	else
	{
		/* Maps */
		if (o_ptr->sval >= 200)
		{
			int i, n;
			char buf[80], fil[20];

			strnfmt(fil, 20, "book-%d.txt", o_ptr->sval);

			n = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, -1));

			/* Parse all the fields */
			for (i = 0; i < n; i += 4)
			{
				/* Grab the fields */
				int x = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 0));
				int y = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 1));
				int w = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 2));
				int h = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 3));

				reveal_wilderness_around_player(y, x, h, w);
			}
		}

		/* Normal parchements */
		else
		{
			/* Save screen */
			screen_save();

			/* Get the filename */
			q = format("book-%d.txt", o_ptr->sval);

			/* Peruse the help file */
			(void)show_file(q, NULL, 0, 0);

			/* Load screen */
			screen_load();

			if (o_ptr->sval >= 100)
			{
				inscription_info[o_ptr->sval - 100].know = TRUE;
			}

			used_up = FALSE;
		}
	}


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

	/* The item was tried */
	object_tried(o_ptr);

	/* An identification was made */
	if (ident && !object_aware_p(o_ptr))
	{
		object_aware(o_ptr);
		gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
	}

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


	/* Hack -- allow certain scrolls to be "preserved" */
	if (!used_up) return;

	sound(SOUND_SCROLL);

	/* Destroy a scroll in the pack */
	if (item >= 0)
	{
		inven_item_increase(item, -1);
		inven_item_describe(item);
		inven_item_optimize(item);
	}

	/* Destroy a scroll on the floor */
	else
	{
		floor_item_increase(0 - item, -1);
		floor_item_describe(0 - item);
		floor_item_optimize(0 - item);
	}

	if (get_skill(SKILL_ALCHEMY))
	{
		if (item >= 0)
		{
			q_ptr = &forge;
			object_prep(q_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_NOTHING));
			object_aware(q_ptr);
			object_known(q_ptr);

			q_ptr->ident |= IDENT_STOREB;

			(void)inven_carry(q_ptr, FALSE);
		}
	}
}



/* Set the 'stick mode' on */
void set_stick_mode(object_type *o_ptr)
{
	s32b bonus = o_ptr->pval3 & 0xFFFF;
	s32b max = o_ptr->pval3 >> 16;

	exec_lua(format("get_level_use_stick = %d; get_level_max_stick = %d", bonus, max));
}
/* Remove 'stick mode' */
void unset_stick_mode()
{
	exec_lua("get_level_use_stick = -1; get_level_max_stick = -1");
}


/*
 * Use a staff.                        -RAK-
 *
 * One charge of one staff disappears.
 *
 * Hack -- staffs of identify can be "cancelled".
 */
void do_cmd_use_staff(void)
{
	int item, ident, chance;

	s32b obvious, use_charge;

	object_type *o_ptr;

	u32b f1, f2, f3, f4, f5, esp;

	cptr q, s;

	/* No magic */
	if (p_ptr->antimagic)
	{
		msg_print("Your anti-magic field disrupts any magic attempts.");
		return;
	}

	/* Restrict choices to wands */
	item_tester_tval = TV_STAFF;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Staff full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Use which staff? ";
	s = "You have no staff to use.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}


	/* Mega-Hack -- refuse to use a pile from the ground */
	if ((item < 0) && (o_ptr->number > 1))
	{
		msg_print("You must first pick up the staffs.");
		return;
	}

	/* Enter device mode  */
	set_stick_mode(o_ptr);

	/* Take a turn */
	energy_use = 100;

	/* Not identified yet */
	ident = FALSE;

	/* get the chance */
	chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));

	/* Extract object flags */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	/* Is it simple to use ? */
	if (f4 & TR4_EASY_USE)
	{
		chance /= 3;
	}

	/* Give everyone a (slight) chance */
	if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
	{
		chance = USE_DEVICE;
	}

	/* Roll for usage */
	if (magik(chance))
	{
		if (flush_failure) flush();
		msg_print("You failed to use the staff properly.");
		sound(SOUND_FAIL);

		/* Leave device mode  */
		unset_stick_mode();
		return;
	}

	/* Notice empty staffs */
	if (o_ptr->pval <= 0)
	{
		if (flush_failure) flush();
		msg_print("The staff has no charges left.");
		o_ptr->ident |= (IDENT_EMPTY);

		/* Leave device mode  */
		unset_stick_mode();
		return;
	}


	/* Sound */
	sound(SOUND_ZAP);


	/* Analyze the staff */
	call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);

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

	/* Tried the item */
	object_tried(o_ptr);

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


	/* Hack -- some uses are "free" */
	if (!use_charge)
	{
		/* Leave device mode  */
		unset_stick_mode();

		return;
	}

	/* An identification was made */
	if (obvious)
	{
		object_aware(o_ptr);
	}

	/* Use a single charge */
	o_ptr->pval--;

	/* XXX Hack -- unstack if necessary */
	if ((item >= 0) && (o_ptr->number > 1))
	{
		object_type forge;
		object_type *q_ptr;

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

		/* Obtain a local object */
		object_copy(q_ptr, o_ptr);

		/* Modify quantity */
		q_ptr->number = 1;

		/* Restore the charges */
		o_ptr->pval++;

		/* Unstack the used item */
		o_ptr->number--;
		item = inven_carry(q_ptr, FALSE);

		/* Message */
		msg_print("You unstack your staff.");
	}

	/* Describe charges in the pack */
	if (item >= 0)
	{
		inven_item_charges(item);
	}

	/* Describe charges on the floor */
	else
	{
		floor_item_charges(0 - item);
	}

	/* Leave device mode  */
	unset_stick_mode();
}


/*
 * Aim a wand (from the pack or floor).
 *
 * Use a single charge from a single item.
 * Handle "unstacking" in a logical manner.
 *
 * For simplicity, you cannot use a stack of items from the
 * ground.  This would require too much nasty code.
 *
 * There are no wands which can "destroy" themselves, in the p_ptr->inventory
 * or on the ground, so we can ignore this possibility.  Note that this
 * required giving "wand of wonder" the ability to ignore destruction
 * by electric balls.
 *
 * All wands can be "cancelled" at the "Direction?" prompt for free.
 *
 * Note that the basic "bolt" wands do slightly less damage than the
 * basic "bolt" rods, but the basic "ball" wands do the same damage
 * as the basic "ball" rods.
 */
void do_cmd_aim_wand(void)
{
	s32b obvious, use_charge;

	int item, ident, chance, sval;

	object_type *o_ptr;

	cptr q, s;

	u32b f1, f2, f3, f4, f5, esp;


	/* No magic */
	if (p_ptr->antimagic)
	{
		msg_print("Your anti-magic field disrupts any magic attempts.");
		return;
	}

	/* Restrict choices to wands */
	item_tester_tval = TV_WAND;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Wand full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Aim which wand? ";
	s = "You have no wand to aim.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}


	/* Mega-Hack -- refuse to aim a pile from the ground */
	if ((item < 0) && (o_ptr->number > 1))
	{
		msg_print("You must first pick up the wands.");
		return;
	}

	/* Take a turn */
	energy_use = 100;

	/* Not identified yet */
	ident = FALSE;

	/* Enter device mode  */
	set_stick_mode(o_ptr);

	/* get the chance */
	chance = exec_lua(format("return spell_chance(%d)", o_ptr->pval2));

	/* Extract object flags */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	/* Is it simple to use ? */
	if (f4 & TR4_EASY_USE)
	{
		chance /= 3;
	}

	/* Roll for usage */
	if (magik(chance))
	{
		if (flush_failure) flush();
		msg_print("You failed to use the wand properly.");
		sound(SOUND_FAIL);

		/* Leave device mode  */
		unset_stick_mode();
		return;
	}

	/* The wand is already empty! */
	if (o_ptr->pval <= 0)
	{
		if (flush_failure) flush();
		msg_print("The wand has no charges left.");
		o_ptr->ident |= (IDENT_EMPTY);

		/* Leave device mode  */
		unset_stick_mode();
		return;
	}


	/* Sound */
	sound(SOUND_ZAP);


	/* XXX Hack -- Extract the "sval" effect */
	sval = o_ptr->sval;

	/* Analyze the wand */
	call_lua("activate_stick", "(d)", "dd", o_ptr->pval2, &obvious, &use_charge);

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

	/* Mark it as tried */
	object_tried(o_ptr);

	/* Hack -- some uses are "free" */
	if (!use_charge)
	{
		/* Leave device mode  */
		unset_stick_mode();

		return;
	}

	/* An identification was made */
	if (obvious)
	{
		object_aware(o_ptr);
	}

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


	/* Use a single charge */
	o_ptr->pval--;

	/* Describe the charges in the pack */
	if (item >= 0)
	{
		inven_item_charges(item);
	}

	/* Describe the charges on the floor */
	else
	{
		floor_item_charges(0 - item);
	}

	/* Leave device mode  */
	unset_stick_mode();
}






/*
 * Activate (zap) a Rod
 *
 * Unstack fully charged rods as needed.
 *
 * Hack -- rods of perception/genocide can be "cancelled"
 * All rods can be cancelled at the "Direction?" prompt
 */


/*
 * Hook to determine if an object is zapable
 */
static bool_ item_tester_hook_zapable(object_type *o_ptr)
{
	if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_ROD_MAIN)) return (TRUE);

	/* Assume not */
	return (FALSE);
}


/*
 * Hook to determine if an object is attachable
 */
static bool_ item_tester_hook_attachable(object_type *o_ptr)
{
	if ((o_ptr->tval == TV_ROD_MAIN) &&
	                (o_ptr->pval == SV_ROD_NOTHING)) return (TRUE);

	/* Assume not */
	return (FALSE);
}


/*
 * Combine a rod and a rod tip
 */
void zap_combine_rod_tip(object_type *q_ptr, int tip_item)
{
	int item;

	object_type *o_ptr;

	object_kind *k_ptr;

	cptr q, s;

	u32b f1, f2, f3, f4, f5, esp;
	s32b cost;


	/* No magic */
	if (p_ptr->antimagic)
	{
		msg_print("Your anti-magic field disrupts any magic attempts.");
		return;
	}

	/* Restrict choices to rods */
	item_tester_hook = item_tester_hook_attachable;

	/* Get an item */
	q = "Attach the rod tip with which rod? ";
	s = "You have no rod to attach to.";
	if (!get_item(&item, q, s, (USE_INVEN))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
		k_ptr = &k_info[o_ptr->k_idx];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
		k_ptr = &k_info[o_ptr->k_idx];
	}

	/* Examine the rod */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	/* Calculate rod tip's mana cost */
	cost = q_ptr->pval;

	if (f4 & TR4_CHEAPNESS)
	{
		cost /= 2;
	}

	/*
	 * The rod must have at least the same mana capacity as the
	 * rod tip spell needs
	 */
	if (o_ptr->pval2 < cost)
	{
		msg_print("This rod doesn't have enough mana for the rod tip.");
		return;
	}

	/* Attach the tip to the rod */
	o_ptr->pval = q_ptr->sval;

	/* Destroy a rod tip in the pack */
	if (tip_item >= 0)
	{
		inven_item_increase(tip_item, -1);
		inven_item_describe(tip_item);
		inven_item_optimize(tip_item);
	}
	/* Destroy a rod tip on the floor */
	else
	{
		floor_item_increase(0 - tip_item, -1);
		floor_item_describe(0 - tip_item);
		floor_item_optimize(0 - tip_item);
	}
}


/*
 * Zap a rod, or attack a rod tip to a rod
 */
void do_cmd_zap_rod(void)
{
	int item, ident, chance, dir, lev;

	int cost;

	bool_ require_dir;

	object_type *o_ptr;

	object_kind *k_ptr;

	object_kind *tip_ptr;

	u32b f1, f2, f3, f4, f5, esp;

	cptr q, s;

	/* Hack -- let perception get aborted */
	bool_ use_charge = TRUE;


	/* No magic */
	if (p_ptr->antimagic)
	{
		msg_print("Your anti-magic field disrupts any magic attempts.");
		return;
	}


	/* Restrict choices to rods */
	item_tester_hook = item_tester_hook_zapable;

	/* Set up the extra finder */
	get_item_hook_find_obj_what = "Rod full name? ";
	get_item_extra_hook = get_item_hook_find_obj;

	/* Get an item */
	q = "Zap which rod? ";
	s = "You have no rod to zap.";
	if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
		k_ptr = &k_info[o_ptr->k_idx];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
		k_ptr = &k_info[o_ptr->k_idx];
	}


	/* "Zapping" a Rod Tip on rod of nothing will attach it */
	if (o_ptr->tval == TV_ROD)
	{
		if (item >= 0)
		{
			zap_combine_rod_tip(o_ptr, item);
			return;
		}
		else
		{
			msg_print("You can't zap a rod tip that's on the floor.");
			return;
		}
	}


	/* Non-directed rods */
	if (o_ptr->pval < SV_ROD_MIN_DIRECTION)
	{
		require_dir = FALSE;
	}

	/* Some rods always require direction */
	else
	{
		switch (o_ptr->pval)
		{
		case SV_ROD_DETECT_TRAP:
		case SV_ROD_HAVOC:
		case SV_ROD_HOME:
			{
				require_dir = FALSE;
				break;
			}

		default:
			{
				require_dir = TRUE;
				break;
			}
		}
	}

	/* Get a direction (unless KNOWN not to need it) */
	if (!object_aware_p(o_ptr) || require_dir)
	{
		/* Get a direction, allow cancel */
		if (!get_aim_dir(&dir)) return;
	}

	/* Take a turn */
	energy_use = 100;

	/* Examine the rod */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	if (f4 & TR4_FAST_CAST) energy_use /= 2;

	/* Not identified yet */
	ident = FALSE;

	/* Extract the item level */
	tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
	lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level;

	/* Base chance of success */
	chance = p_ptr->skill_dev;

	/* Confusion hurts skill */
	if (p_ptr->confused) chance = chance / 2;

	/* High level objects are harder */
	chance = chance - ((lev > 50) ? 50 : lev);

	if (chance <= 0)
	{
		chance = 1;
	}

	/* Is it simple to use ? */
	if (f4 & TR4_EASY_USE)
	{
		chance *= 10;
	}

	/* Give everyone a (slight) chance */
	if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
	{
		chance = USE_DEVICE;
	}

	/* Roll for usage */
	if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
	{
		/* Flush input if necessary */
		if (flush_failure) flush();

		/* Message */
		msg_print("You failed to use the rod properly.");

		sound(SOUND_FAIL);

		return;
	}

	/* Extract mana cost */
	cost = tip_ptr->pval;

	/* "Cheapness" ego halven the cost */
	if (f4 & TR4_CHEAPNESS) cost = cost / 2;

	/* A single rod is still charging */
	if (o_ptr->timeout < cost)
	{
		/* Flush input if necessary */
		if (flush_failure) flush();

		/* Message */
		msg_print("The rod does not have enough mana yet.");

		return;
	}

	/* Increase the timeout by the rod kind's pval. */
	o_ptr->timeout -= cost;

	/* Sound */
	sound(SOUND_ZAP);

	/* Analyze the rod */
	switch (o_ptr->pval)
	{
	case SV_ROD_HOME:
		{
			ident = TRUE;

			do_cmd_home_trump();

			break;
		}

	case SV_ROD_DETECT_TRAP:
		{
			if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;

			break;
		}

	case SV_ROD_DETECT_DOOR:
		{
			if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
			if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;

			break;
		}

	case SV_ROD_IDENTIFY:
		{
			ident = TRUE;

			if (!ident_spell()) use_charge = FALSE;

			break;
		}

	case SV_ROD_RECALL:
		{
			if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
			{
				use_charge = FALSE;
			}
			else
			{
				recall_player(21, 15);

				ident = TRUE;
			}

			break;
		}

	case SV_ROD_ILLUMINATION:
		{
			if (lite_area(damroll(2, 8), 2)) ident = TRUE;

			break;
		}

	case SV_ROD_MAPPING:
		{
			map_area();

			ident = TRUE;

			break;
		}

	case SV_ROD_DETECTION:
		{
			detect_all(DEFAULT_RADIUS);

			ident = TRUE;

			break;
		}

	case SV_ROD_PROBING:
		{
			probing();

			ident = TRUE;

			break;
		}

	case SV_ROD_CURING:
		{
			if (set_blind(0)) ident = TRUE;
			if (set_poisoned(0)) ident = TRUE;
			if (set_confused(0)) ident = TRUE;
			if (set_stun(0)) ident = TRUE;
			if (set_cut(0)) ident = TRUE;
			if (set_image(0)) ident = TRUE;

			break;
		}

	case SV_ROD_HEALING:
		{
			if (hp_player(500)) ident = TRUE;
			if (set_stun(0)) ident = TRUE;
			if (set_cut(0)) ident = TRUE;

			break;
		}

	case SV_ROD_RESTORATION:
		{
			if (restore_level()) ident = TRUE;
			if (do_res_stat(A_STR, TRUE)) ident = TRUE;
			if (do_res_stat(A_INT, TRUE)) ident = TRUE;
			if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
			if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
			if (do_res_stat(A_CON, TRUE)) ident = TRUE;
			if (do_res_stat(A_CHR, TRUE)) ident = TRUE;

			break;
		}

	case SV_ROD_SPEED:
		{
			if (!p_ptr->fast)
			{
				if (set_fast(randint(30) + 15, 10)) ident = TRUE;
			}
			else
			{
				(void)set_fast(p_ptr->fast + 5, 10);
			}

			break;
		}

	case SV_ROD_TELEPORT_AWAY:
		{
			if (teleport_monster(dir)) ident = TRUE;

			break;
		}

	case SV_ROD_DISARMING:
		{
			if (disarm_trap(dir)) ident = TRUE;

			break;
		}

	case SV_ROD_LITE:
		{
			msg_print("A line of blue shimmering light appears.");
			lite_line(dir);

			ident = TRUE;

			break;
		}

	case SV_ROD_SLEEP_MONSTER:
		{
			if (sleep_monster(dir)) ident = TRUE;

			break;
		}

	case SV_ROD_SLOW_MONSTER:
		{
			if (slow_monster(dir)) ident = TRUE;

			break;
		}

	case SV_ROD_DRAIN_LIFE:
		{
			if (drain_life(dir, 75)) ident = TRUE;

			break;
		}

	case SV_ROD_POLYMORPH:
		{
			if (poly_monster(dir)) ident = TRUE;

			break;
		}

	case SV_ROD_ACID_BOLT:
		{
			fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8));

			ident = TRUE;

			break;
		}

	case SV_ROD_ELEC_BOLT:
		{
			fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8));

			ident = TRUE;

			break;
		}

	case SV_ROD_FIRE_BOLT:
		{
			fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8));

			ident = TRUE;

			break;
		}

	case SV_ROD_COLD_BOLT:
		{
			fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8));

			ident = TRUE;

			break;
		}

	case SV_ROD_ACID_BALL:
		{
			fire_ball(GF_ACID, dir, 60, 2);

			ident = TRUE;

			break;
		}

	case SV_ROD_ELEC_BALL:
		{
			fire_ball(GF_ELEC, dir, 32, 2);

			ident = TRUE;

			break;
		}

	case SV_ROD_FIRE_BALL:
		{
			fire_ball(GF_FIRE, dir, 72, 2);

			ident = TRUE;

			break;
		}

	case SV_ROD_COLD_BALL:
		{
			fire_ball(GF_COLD, dir, 48, 2);

			ident = TRUE;

			break;
		}

	case SV_ROD_HAVOC:
		{
			call_chaos();

			ident = TRUE;

			break;
		}

	default:
		{
			process_hooks(HOOK_ZAP, "(d,d)", o_ptr->tval, o_ptr->sval);

			break;
		}
	}


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

	/* Tried the object */
	object_tried(o_ptr);

	/* Successfully determined the object function */
	if (ident && !object_aware_p(o_ptr))
	{
		object_aware(o_ptr);
		gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
	}

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

	/* Hack -- deal with cancelled zap */
	if (!use_charge)
	{
		o_ptr->timeout += cost;

		return;
	}
}




/*
 * Hook to determine if an object is activable
 */
static bool_ item_tester_hook_activate(object_type *o_ptr)
{
	u32b f1, f2, f3, f4, f5, esp;


	/* Not known */
	if (!object_known_p(o_ptr)) return (FALSE);

	/* Extract the flags */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	/* Check activation flag */
	if (f3 & (TR3_ACTIVATE)) return (TRUE);

	/* Assume not */
	return (FALSE);
}



/*
 * Hack -- activate the ring of power
 */
int ring_of_power()
{
	char ch = 0, p = 0;

	int plev = p_ptr->lev;

	int timeout = 0;


	/* Select power to use */
	while (TRUE)
	{
		if (!get_com("[S]ummon a wraith, [R]ule the world or "
		                "[C]ast a powerful attack? ", &ch))
		{
			return (0);
		}

		if (ch == 'S' || ch == 's')
		{
			p = 1;
			break;
		}
		if (ch == 'R' || ch == 'r')
		{
			p = 2;
			break;
		}
		if (ch == 'C' || ch == 'c')
		{
			p = 3;
			break;
		}
	}

	/* Summon a Wraith */
	if (p == 1)
	{
		/* Rewrite this -- pelpel */
		if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
		                             (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
		                             (bool_)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
		{
			msg_print("Cold winds begin to blow around you, "
			          "carrying with them the stench of decay...");
			msg_print("Ancient, long-dead forms arise from the ground "
			          "to serve you!");
		}
		timeout = 200 + rand_int(200);
	}

	/* Rule the World -- only if we can really do so */
	else if (p == 2)
	{
		msg_print("The power of the ring destroys the world!");
		msg_print("The world changes!");

		if (autosave_l)
		{
			is_autosave = TRUE;
			msg_print("Autosaving the game...");
			do_cmd_save_game();
			is_autosave = FALSE;
		}

		/* Leaving */
		p_ptr->leaving = TRUE;
		timeout = 250 + rand_int(250);
	}

	/* Cast a powerful spell */
	else if (p == 3)
	{
		int dir;

		if (!get_aim_dir(&dir)) return (0);

		if (rand_int(3) == 0)
		{
			msg_print("You call the fire of Mount Doom!");
			fire_ball(GF_METEOR, dir, 600, 4);
		}
		else
		{
			msg_print("Your ring tries to take possession of your enemy's mind!");
			fire_bolt(GF_CHARM, dir, 600);
		}
		timeout = 300 + rand_int(300);
	}

	return (timeout);
}




/*
 * Enchant some bolts
 */
bool_ brand_bolts(void)
{
	int i;


	/* Use the first acceptable bolts */
	for (i = 0; i < INVEN_PACK; i++)
	{
		object_type *o_ptr = &p_ptr->inventory[i];

		/* Skip non-bolts */
		if (o_ptr->tval != TV_BOLT) continue;

		/* Skip artifacts and ego-items */
		if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue;

		/* Skip cursed/broken items */
		if (cursed_p(o_ptr)) continue;

		/* Randomize */
		if (rand_int(100) < 75) continue;

		/* Message */
		msg_print("Your bolts are covered in a fiery aura!");

		/* Ego-item */
		o_ptr->name2 = EGO_FLAME;

		/* Apply the ego */
		apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);

		/* Enchant */
		enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);

		/* Notice */
		return (TRUE);
	}

	/* Flush */
	if (flush_failure) flush();

	/* Fail */
	msg_print("The fiery enchantment failed.");

	/* Notice */
	return (TRUE);
}


/*
 * Objects in the p_ptr->inventory can now be activated, and
 * SOME of those may be able to stack (ego wands or something)
 * in any case, we can't know that it's impossible. *BUT* we'll
 * ignore it for now, and the timeout will be set on the entire stack
 * of objects. Reduces their utility, but oh well.
 *
 * Note that it always takes a turn to activate an object, even if
 * the user hits "escape" at the "direction" prompt.
 */
void do_cmd_activate(void)
{
	int item, lev, chance;

	char ch, spell_choice;

	object_type *o_ptr;

	u32b f1, f2, f3, f4, f5, esp;

	cptr q, s;


	/* Prepare the hook */
	item_tester_hook = item_tester_hook_activate;

	/* Get an item */
	command_see = TRUE;
	command_wrk = USE_EQUIP;
	q = "Activate which item? ";
	s = "You have nothing to activate.";
	if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return;

	/* Get the item (in the pack) */
	if (item >= 0)
	{
		o_ptr = &p_ptr->inventory[item];
	}

	/* Get the item (on the floor) */
	else
	{
		o_ptr = &o_list[0 - item];
	}

	/* Extract object flags */
	object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);

	/* Wearable items have to be worn */
	if (!(f5 & TR5_ACTIVATE_NO_WIELD))
	{
		if (item < INVEN_WIELD)
		{
			msg_print("You must wear it to activate it.");
			return;
		}
	}

	/* Take a turn */
	energy_use = 100;

	/* Extract the item level */
	lev = k_info[o_ptr->k_idx].level;

	/* Hack -- Use artifact level instead */
	if (artifact_p(o_ptr))
	{
		if (o_ptr->tval == TV_RANDART)
		{
			lev = random_artifacts[o_ptr->sval].level;
		}
		else
		{
			lev = a_info[o_ptr->name1].level;
		}
	}

	/* Base chance of success */
	chance = p_ptr->skill_dev;

	/* Confusion hurts skill */
	if (p_ptr->confused) chance = chance / 2;

	/* Hight level objects are harder */
	chance = chance - ((lev > 50) ? 50 : lev);

	if (chance <= 0)
	{
		chance = 1;
	}

	/* Is it simple to use ? */
	if (f4 & TR4_EASY_USE)
	{
		chance *= 10;
	}

	/* Give everyone a (slight) chance */
	if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
	{
		chance = USE_DEVICE;
	}

	/* Roll for usage */
	if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
	{
		if (flush_failure) flush();
		msg_print("You failed to activate it properly.");
		sound(SOUND_FAIL);
		return;
	}

	/* Check the recharge */
	if (o_ptr->timeout)
	{
		/* Mage Staff of Spells -- Have another timeout in xtra2 */
		if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2)
		{
			msg_print("It whines, glows and fades...");
			return;
		}

		/* Monster eggs */
		else if (o_ptr->tval == TV_EGG)
		{
			msg_print("You resume the development of the egg.");
			o_ptr->timeout = 0;

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

			/* Success */
			return;
		}

		/* Normal activatable items */
		else
		{
			msg_print("It whines, glows and fades...");
			return;
		}
	}


	/* Activate the item */
	msg_print("You activate it...");

	/* Sound */
	sound(SOUND_ZAP);

	/* Lua hook ? -- go first to allow lua to override */
	if (process_hooks(HOOK_ACTIVATE, "(d)", item))
	{
		return;
	}

	/* New mostly unified activation code
	   This has to be early to allow artifacts to override normal items -- neil */

	if ( activation_aux(o_ptr, TRUE, item) == NULL )
	{
		/* Window stuff */
		p_ptr->window |= (PW_INVEN | PW_EQUIP);

		/* Success */
		return;
	}

	/* Mage Staff of Spells */
	if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL))
	{
		while (TRUE)
		{
			if (!get_com("Use Spell [1] or [2]?", &ch))
			{
				return;
			}

			if (ch == '1')
			{
				spell_choice = 1;
				break;
			}

			if (ch == '2')
			{
				spell_choice = 2;
				break;
			}
		}

		if (spell_choice == 1)
		{
			/* Still need to check timeouts because there is another counter */
			if (o_ptr->timeout)
			{
				msg_print("The first spell is still charging!");
				return;
			}

			/* Cast spell 1 */
			activate_spell(o_ptr, spell_choice);
		}
		else if (spell_choice == 2)
		{
			/* Still need to check timeouts because there is another counter */
			if (o_ptr->xtra2)
			{
				msg_print("The second spell is still charging!");
				return;
			}

			/* Cast spell 2 */
			activate_spell(o_ptr, spell_choice);
		}

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

		/* Success */
		return;
	}

	/* Monster eggs */
	if (o_ptr->tval == TV_EGG)
	{
		msg_print("You stop the development of the egg.");
		o_ptr->timeout = -1;

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

		/* Success */
		return;
	}

	/* Musical instruments */
	if (o_ptr->tval == TV_INSTRUMENT)
	{
		/* Horns */
		if (o_ptr->sval == SV_HORN)
		{
			msg_format("Your instrument emits a loud sound!");

			aggravate_monsters(1);

			o_ptr->timeout = 100;
		}

		/* Success */
		return;
	}

	/* Mistake */
	msg_print("Oops.  That object cannot be activated.");
}



const char *activation_aux(object_type * o_ptr, bool_ doit, int item)
{
	int plev = get_skill(SKILL_DEVICE);

	int i = 0, ii = 0, ij = 0, k, dir, dummy = 0;
	int chance;
	bool_ is_junkart = (o_ptr->tval == TV_RANDART);

	int spell = 0;

	/* Junkarts */
	if (is_junkart)
		spell = activation_info[o_ptr->pval2].spell;

	/* True Actifacts */
	if (!spell && o_ptr->name1)
		spell = a_info[o_ptr->name1].activate;

	/* Random and Alchemist Artifacts */
	if (!spell && o_ptr->art_name)
		spell = o_ptr->xtra2;

	/* Ego Items */
	if (!spell && o_ptr->name2)
		spell = e_info[o_ptr->name2].activate;

	/* Dual egos with the second ego having the activation */
	if (!spell && o_ptr->name2b)
		spell = e_info[o_ptr->name2b].activate;

	/* Intrinsic to item type (rings of Ice, etc) */
	if (!spell)
		spell = k_info[o_ptr->k_idx].activate;

	/* Complain about mis-configured .txt files? */
	if (!spell)
		return "Unknown!";

	/* Negative means a unified spell index */
	if (spell < 0)
	{
		if (doit)
		{
			call_lua("activate_activation", "(d,d)", "", -spell, item);
			o_ptr->timeout = exec_lua(format("return get_activation_timeout(%d)", -spell));
		}
		else
		{
			return string_exec_lua(format("return get_activation_desc(%d)", -spell));
		}
	}
	else
	{
		/* Activate for attack */
		switch (spell)
		{
		case ACT_GILGALAD:
			{
				if (!doit) return "starlight (75) every 75+d75 turns";
				for (k = 1; k < 10; k++)
				{
					if (k - 5) fire_beam(GF_LITE, k, 75);
				}

				o_ptr->timeout = rand_int(75) + 75;

				break;
			}

		case ACT_CELEBRIMBOR:
			{
				if (!doit) return "temporary ESP (dur 20+d20) every 20+d50 turns";
				set_tim_esp(p_ptr->tim_esp + randint(20) + 20);

				o_ptr->timeout = rand_int(50) + 20;

				break;
			}

		case ACT_SKULLCLEAVER:
			{
				if (!doit) return "destruction every 200+d200 turns";
				destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE);

				o_ptr->timeout = rand_int(200) + 200;

				break;
			}

		case ACT_HARADRIM:
			{
				if (!doit) return "berserk strength every 50+d50 turns";
				set_afraid(0);
				set_shero(p_ptr->shero + randint(25) + 25);
				hp_player(30);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}

		case ACT_FUNDIN:
			{
				if (!doit) return "dispel evil (x4) every 100+d100 turns";
				dispel_evil(p_ptr->lev * 4);

				o_ptr->timeout = rand_int(100) + 100;

				break;
			}

		case ACT_EOL:
			{
				if (!doit) return "mana bolt (9d8) 7+d7 turns";
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_MANA, dir, damroll(9, 8));

				o_ptr->timeout = rand_int(7) + 7;

				break;
			}

		case ACT_UMBAR:
			{
				if (!doit) return "magic arrow (10d10) every 20+d20 turns";
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_MISSILE, dir, damroll(10, 10));

				o_ptr->timeout = rand_int(20) + 20;

				break;
			}

		case ACT_NUMENOR:
			{
				/* Give full knowledge */
				/* Hack -- Maximal info */
				monster_race *r_ptr;
				cave_type *c_ptr;
				int x, y, m;

				if (!doit) return "analyze monster every 500+d200 turns";

				if (!tgt_pt(&x, &y)) break;

				c_ptr = &cave[y][x];
				if (!c_ptr->m_idx) break;

				r_ptr = &r_info[c_ptr->m_idx];

				/* Observe "maximal" attacks */
				for (m = 0; m < 4; m++)
				{
					/* Examine "actual" blows */
					if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
					{
						/* Hack -- maximal observations */
						r_ptr->r_blows[m] = MAX_UCHAR;
					}
				}

				/* Hack -- maximal drops */
				r_ptr->r_drop_gold = r_ptr->r_drop_item =
				                             (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
				                              ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
				                              ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
				                              ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
				                              ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
				                              ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));

				/* Hack -- but only "valid" drops */
				if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
				if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;

				/* Hack -- observe many spells */
				r_ptr->r_cast_inate = MAX_UCHAR;
				r_ptr->r_cast_spell = MAX_UCHAR;

				/* Hack -- know all the flags */
				r_ptr->r_flags1 = r_ptr->flags1;
				r_ptr->r_flags2 = r_ptr->flags2;
				r_ptr->r_flags3 = r_ptr->flags3;
				r_ptr->r_flags4 = r_ptr->flags4;
				r_ptr->r_flags5 = r_ptr->flags5;
				r_ptr->r_flags6 = r_ptr->flags6;
				r_ptr->r_flags7 = r_ptr->flags7;
				r_ptr->r_flags8 = r_ptr->flags8;
				r_ptr->r_flags9 = r_ptr->flags9;

				o_ptr->timeout = rand_int(200) + 500;

				break;
			}

		case ACT_KNOWLEDGE:
			{
				if (!doit) return "whispers from beyond(sanity drain) 100+d200 turns";
				identify_fully();
				take_sanity_hit(damroll(10, 7), "the sounds of the dead");

				o_ptr->timeout = rand_int(200) + 100;

				break;
			}

		case ACT_UNDEATH:
			{
				if (!doit) return "ruination every 10+d10 turns";
				msg_print("The phial wells with dark light...");
				unlite_area(damroll(2, 15), 3);
				take_hit(damroll(10, 10), "activating The Phial of Undeath");
				(void)dec_stat(A_DEX, 25, STAT_DEC_PERMANENT);
				(void)dec_stat(A_WIS, 25, STAT_DEC_PERMANENT);
				(void)dec_stat(A_CON, 25, STAT_DEC_PERMANENT);
				(void)dec_stat(A_STR, 25, STAT_DEC_PERMANENT);
				(void)dec_stat(A_CHR, 25, STAT_DEC_PERMANENT);
				(void)dec_stat(A_INT, 25, STAT_DEC_PERMANENT);

				o_ptr->timeout = rand_int(10) + 10;

				break;
			}

		case ACT_THRAIN:
			{
				if (!doit) return "detection every 30+d30 turns";
				msg_print("The stone glows a deep green...");
				detect_all(DEFAULT_RADIUS);

				o_ptr->timeout = rand_int(30) + 30;

				break;
			}

		case ACT_BARAHIR:
			{
				if (!doit) return "dispel small life every 55+d55 turns";
				msg_print("You exterminate small life.");
				(void)dispel_monsters(4);

				o_ptr->timeout = rand_int(55) + 55;

				break;
			}

		case ACT_TULKAS:
			{
				if (!doit) return "haste self (75+d75 turns) every 150+d150 turns";
				msg_print("The ring glows brightly...");
				if (!p_ptr->fast)
				{
					(void)set_fast(randint(75) + 75, 10);
				}
				else
				{
					(void)set_fast(p_ptr->fast + 5, 10);
				}

				o_ptr->timeout = rand_int(150) + 150;

				break;
			}

		case ACT_NARYA:
			{
				if (!doit) return "healing (500) every 200+d100 turns";
				msg_print("The ring glows deep red...");
				hp_player(500);
				set_blind(0);
				set_confused(0);
				set_poisoned(0);
				set_stun(0);
				set_cut(0);

				o_ptr->timeout = rand_int(100) + 200;

				break;
			}

		case ACT_NENYA:
			{
				if (!doit) return "healing (800) every 100+d200 turns";
				msg_print("The ring glows bright white...");
				hp_player(800);
				set_blind(0);
				set_confused(0);
				set_poisoned(0);
				set_stun(0);
				set_cut(0);

				o_ptr->timeout = rand_int(200) + 100;

				break;
			}

		case ACT_VILYA:
			{
				if (!doit) return "greater healing (900) every 200+d200 turns";
				msg_print("The ring glows deep blue...");
				hp_player(900);
				set_blind(0);
				set_confused(0);
				set_poisoned(0);
				set_stun(0);
				set_cut(0);
				if (p_ptr->black_breath)
				{
					p_ptr->black_breath = FALSE;
					msg_print("The hold of the Black Breath on you is broken!");
				}

				o_ptr->timeout = rand_int(200) + 200;

				break;
			}

		case ACT_POWER:
			{
				if (!doit) return "powerful things";
				msg_print("The ring glows intensely black...");

				o_ptr->timeout = ring_of_power();

				break;
			}


			/* The Stone of Lore is perilous, for the sake of game balance. */
		case ACT_STONE_LORE:
			{
				if (!doit) return "perilous identify every turn";
				msg_print("The stone reveals hidden mysteries...");
				if (!ident_spell()) break;

				if (has_ability(AB_PERFECT_CASTING))
				{
					/* Sufficient mana */
					if (20 <= p_ptr->csp)
					{
						/* Use some mana */
						p_ptr->csp -= 20;
					}

					/* Over-exert the player */
					else
					{
						int oops = 20 - p_ptr->csp;

						/* No mana left */
						p_ptr->csp = 0;
						p_ptr->csp_frac = 0;

						/* Message */
						msg_print("You are too weak to control the stone!");

						/* Hack -- Bypass free action */
						(void)set_paralyzed(p_ptr->paralyzed +
						                    randint(5 * oops + 1));

						/* Confusing. */
						(void)set_confused(p_ptr->confused +
						                   randint(5 * oops + 1));
					}

					/* Redraw mana */
					p_ptr->redraw |= (PR_MANA);
				}

				take_hit(damroll(1, 12), "perilous secrets");

				/* Confusing. */
				if (rand_int(5) == 0)
				{
					(void)set_confused(p_ptr->confused + randint(10));
				}

				/* Exercise a little care... */
				if (rand_int(20) == 0)
				{
					take_hit(damroll(4, 10), "perilous secrets");
				}

				o_ptr->timeout = 1;

				break;
			}

		case ACT_RAZORBACK:
			{
				if (!doit) return "star ball (150) every 1000 turns";
				msg_print("Your armor is surrounded by lightning...");
				for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3);

				o_ptr->timeout = 1000;

				break;
			}

		case ACT_BLADETURNER:
			{
				if (!doit) return "invulnerability (4+d8) every 800 turns";
				set_invuln(p_ptr->invuln + randint(8) + 4);

				o_ptr->timeout = 800;

				break;
			}

		case ACT_MEDIATOR:
			{
				if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe the elements.");
				fire_ball(GF_MISSILE, dir, 300, 4);
				msg_print("Your armor glows many colours...");
				(void)set_afraid(0);
				(void)set_shero(p_ptr->shero + randint(50) + 50);
				(void)hp_player(30);
				(void)set_blessed(p_ptr->blessed + randint(50) + 50);
				(void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50);
				(void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50);
				(void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50);
				(void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50);
				(void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_BELEGENNON:
			{
				if (!doit) return ("heal (777), curing and heroism every 300 turns");
				msg_print("A heavenly choir sings...");
				(void)set_poisoned(0);
				(void)set_cut(0);
				(void)set_stun(0);
				(void)set_confused(0);
				(void)set_blind(0);
				(void)set_hero(p_ptr->hero + randint(25) + 25);
				(void)hp_player(777);

				o_ptr->timeout = 300;

				break;
			}

		case ACT_GORLIM:
			{
				if (!doit) return "rays of fear in every direction";
				turn_monsters(40 + p_ptr->lev);

				o_ptr->timeout = 3 * (p_ptr->lev + 10);

				break;
			}

		case ACT_COLLUIN:
			{
				if (!doit) return "resistance (20+d20 turns) every 111 turns";
				msg_print("Your cloak glows many colours...");
				(void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
				(void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
				(void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
				(void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
				(void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);

				o_ptr->timeout = 111;

				break;
			}


		case ACT_BELANGIL:
			{
				if (!doit) return "frost ball (48) every 5+d5 turns";
				msg_print("Your dagger is covered in frost...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_COLD, dir, 48, 2);

				o_ptr->timeout = rand_int(5) + 5;

				break;
			}

		case ACT_ANGUIREL:
			{
				if (!doit) return "a getaway every 35 turns";
				switch (randint(13))
				{
				case 1:
				case 2:
				case 3:
				case 4:
				case 5:
					{
						teleport_player(10);

						break;
					}

				case 6:
				case 7:
				case 8:
				case 9:
				case 10:
					{
						teleport_player(222);

						break;
					}

				case 11:
				case 12:
					{
						(void)stair_creation();

						break;
					}

				default:
					{
						if (get_check("Leave this level? "))
						{
							if (autosave_l)
							{
								is_autosave = TRUE;
								msg_print("Autosaving the game...");
								do_cmd_save_game();
								is_autosave = FALSE;
							}

							/* Leaving */
							p_ptr->leaving = TRUE;
						}

						break;
					}
				}

				o_ptr->timeout = 35;

				break;
			}

		case ACT_ERU:
			{
				if (!doit) return "healing(7000), curing every 500 turns";
				msg_print("Your sword glows an intense white...");
				hp_player(7000);
				heal_insanity(50);
				set_blind(0);
				set_poisoned(0);
				set_confused(0);
				set_stun(0);
				set_cut(0);
				set_image(0);

				o_ptr->timeout = 500;

				break;
			}

		case ACT_DAWN:
			{
				if (!doit) return "summon the Legion of the Dawn every 500+d500 turns";
				msg_print("You summon the Legion of the Dawn.");
				(void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE);

				o_ptr->timeout = 500 + randint(500);

				break;
			}

		case ACT_FIRESTAR:
			{
				if (!doit) return "large fire ball (72) every 100 turns";
				msg_print("Your morning star rages in fire...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_FIRE, dir, 72, 3);

				o_ptr->timeout = 100;

				break;
			}

		case ACT_TURMIL:
			{
				if (!doit) return "drain life (90) every 70 turns";
				msg_print("Your hammer glows white...");
				if (!get_aim_dir(&dir)) break;
				drain_life(dir, 90);

				o_ptr->timeout = 70;

				break;
			}

		case ACT_CUBRAGOL:
			{
				if (!doit) return "fire branding of bolts every 999 turns";
				msg_print("Your crossbow glows deep red...");
				(void)brand_bolts();

				o_ptr->timeout = 999;

				break;
			}

		case ACT_ELESSAR:
			{
				if (!doit) return "heal and cure black breath every 200 turns";
				if (p_ptr->black_breath)
				{
					msg_print("The hold of the Black Breath on you is broken!");
				}
				p_ptr->black_breath = FALSE;
				hp_player(100);

				o_ptr->timeout = 200;

				break;
			}

		case ACT_GANDALF:
			{
				if (!doit) return "restore mana every 666 turns";
				msg_print("Your mage staff glows deep blue...");
				if (p_ptr->csp < p_ptr->msp)
				{
					p_ptr->csp = p_ptr->msp;
					p_ptr->csp_frac = 0;
					msg_print("Your feel your head clear.");
					p_ptr->redraw |= (PR_MANA);
					p_ptr->window |= (PW_PLAYER);
				}

				o_ptr->timeout = 666;

				break;
			}

		case ACT_MARDA:
			{
				if (!doit) return "summon a thunderlord every 1000 turns";
				if (randint(3) == 1)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_THUNDERLORD))
					{
						msg_print("A Thunderlord comes from thin air!");
						msg_print("'I will burn you!'");
					}
				}
				else
				{
					if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
					                             SUMMON_THUNDERLORD, (bool_)(plev == 50 ? TRUE : FALSE)))
					{
						msg_print("A Thunderlord comes from thin air!");
						msg_print("'I will help you in your difficult task.'");
					}
				}

				o_ptr->timeout = 1000;

				break;
			}

		case ACT_PALANTIR:
			{
				if (!doit) return "clairvoyance every 100+d100 turns";
				msg_print("The stone glows a deep green...");
				wiz_lite_extra();
				(void)detect_traps(DEFAULT_RADIUS);
				(void)detect_doors(DEFAULT_RADIUS);
				(void)detect_stairs(DEFAULT_RADIUS);

				o_ptr->timeout = rand_int(100) + 100;

				break;
			}

		case ACT_EREBOR:
			{
				if (!doit) return "open a secret passage every 75 turns";
				msg_print("Your pick twists in your hands.");

				if (!get_aim_dir(&dir)) break;
				if (passwall(dir, TRUE))
				{
					msg_print("A passage opens, and you step through.");
				}
				else
				{
					msg_print("There is no wall there!");
				}

				o_ptr->timeout = 75;

				break;
			}

		case ACT_DRUEDAIN:
			{
				if (!doit) return "detection every 99 turns";
				msg_print("Your drum shows you the world.");
				detect_all(DEFAULT_RADIUS);

				o_ptr->timeout = 99;

				break;
			}

		case ACT_ROHAN:
			{
				if (!doit) return "heroism, berserker, and haste every 250 turns";
				msg_print("Your horn glows deep red.");
				set_afraid(0);
				set_shero(p_ptr->shero + damroll(5, 10) + 30);
				set_afraid(0);
				set_hero(p_ptr->hero + damroll(5, 10) + 30);
				set_fast(p_ptr->fast + damroll(5, 10) + 30, 10);
				hp_player(30);

				o_ptr->timeout = 250;

				break;
			}

		case ACT_HELM:
			{
				if (!doit) return "sound ball (300) every 300 turns";
				msg_print("Your horn emits a loud sound.");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_SOUND, dir, 300, 6);

				o_ptr->timeout = 300;

				break;
			}

		case ACT_BOROMIR:
			{
				if (!doit) return "mass human summoning every 1000 turns";
				msg_print("Your horn calls for help.");
				for (i = 0; i < 15; i++)
				{
					summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE);
				}

				o_ptr->timeout = 1000;

				break;
			}

		case ACT_HURIN:
			{
				if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns";
				if (!p_ptr->fast)
				{
					(void)set_fast(randint(50) + 50, 10);
				}
				else
				{
					(void)set_fast(p_ptr->fast + 5, 10);
				}
				hp_player(30);
				set_afraid(0);
				set_shero(p_ptr->shero + randint(50) + 50);

				o_ptr->timeout = rand_int(200) + 100;

				break;
			}

		case ACT_AXE_GOTHMOG:
			{
				if (!doit) return "fire ball (300) every 200+d200 turns";
				msg_print("Your lochaber axe erupts in fire...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_FIRE, dir, 300, 4);

				o_ptr->timeout = 200 + rand_int(200);

				break;
			}

		case ACT_MELKOR:
			{
				if (!doit) return "darkness ball (150) every 100 turns";
				msg_print("Your spear is covered of darkness...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_DARK, dir, 150, 3);

				o_ptr->timeout = 100;

				break;
			}

		case ACT_GROND:
			{
				if (!doit) return "alter reality every 100 turns";
				msg_print("Your hammer hits the floor...");
				alter_reality();

				o_ptr->timeout = 100;

				break;
			}

		case ACT_NATUREBANE:
			{
				if (!doit) return "dispel monsters (300) every 200+d200 turns";
				msg_print("Your axe glows blood red...");
				dispel_monsters(300);

				o_ptr->timeout = 200 + randint(200);

				break;
			}

		case ACT_NIGHT:
			{
				if (!doit) return "vampiric drain (3*100) every 250 turns";
				msg_print("Your axe emits a black aura...");
				if (!get_aim_dir(&dir)) break;
				for (i = 0; i < 3; i++)
				{
					if (drain_life(dir, 100)) hp_player(100);
				}

				o_ptr->timeout = 250;

				break;
			}

		case ACT_ORCHAST:
			{
				if (!doit) return "detect orcs every 10 turns";
				msg_print("Your weapon glows brightly...");
				(void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS);

				o_ptr->timeout = 10;

				break;
			}
		case ACT_SUNLIGHT:
			{
				if (!doit) return "beam of sunlight every 10 turns";

				if (!get_aim_dir(&dir)) break;
				msg_print("A line of sunlight appears.");
				lite_line(dir);

				o_ptr->timeout = 10;

				break;
			}

		case ACT_BO_MISS_1:
			{
				if (!doit) return "magic missile (2d6) every 2 turns";
				msg_print("It glows extremely brightly...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_MISSILE, dir, damroll(2, 6));

				o_ptr->timeout = 2;

				break;
			}

		case ACT_BA_POIS_1:
			{
				if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns";
				msg_print("It throbs deep green...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_POIS, dir, 12, 3);

				o_ptr->timeout = rand_int(4) + 4;

				break;
			}

		case ACT_BO_ELEC_1:
			{
				if (!doit) return "lightning bolt (4d8) every 6+d6 turns";
				msg_print("It is covered in sparks...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_ELEC, dir, damroll(4, 8));

				o_ptr->timeout = rand_int(6) + 6;

				break;
			}

		case ACT_BO_ACID_1:
			{
				if (!doit) return "acid bolt (5d8) every 5+d5 turns";
				msg_print("It is covered in acid...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_ACID, dir, damroll(5, 8));

				o_ptr->timeout = rand_int(5) + 5;

				break;
			}

		case ACT_BO_COLD_1:
			{
				if (!doit) return "frost bolt (6d8) every 7+d7 turns";
				msg_print("It is covered in frost...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_COLD, dir, damroll(6, 8));

				o_ptr->timeout = rand_int(7) + 7;

				break;
			}

		case ACT_BO_FIRE_1:
			{
				if (!doit) return "fire bolt (9d8) every 8+d8 turns";
				msg_print("It is covered in fire...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_FIRE, dir, damroll(9, 8));

				o_ptr->timeout = rand_int(8) + 8;

				break;
			}

		case ACT_BA_COLD_1:
			{
				if (!doit) return "ball of cold (48) every 400 turns";
				msg_print("It is covered in frost...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_COLD, dir, 48, 2);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_BA_FIRE_1:
			{
				if (!doit) return "ball of fire (72) every 400 turns";
				msg_print("It glows an intense red...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_FIRE, dir, 72, 2);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_DRAIN_1:
			{
				if (!doit) return "drain life (100) every 100+d100 turns";
				msg_print("It glows black...");
				if (!get_aim_dir(&dir)) break;
				if (drain_life(dir, 100))

					o_ptr->timeout = rand_int(100) + 100;

				break;
			}

		case ACT_BA_COLD_2:
			{
				if (!doit) return "ball of cold (100) every 300 turns";
				msg_print("It glows an intense blue...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_COLD, dir, 100, 2);

				o_ptr->timeout = 300;

				break;
			}

		case ACT_BA_ELEC_2:
			{
				if (!doit) return "ball of lightning (100) every 500 turns";
				msg_print("It crackles with electricity...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_ELEC, dir, 100, 3);

				o_ptr->timeout = 500;

				break;
			}

		case ACT_DRAIN_2:
			{
				if (!doit) return "drain life (120) every 400 turns";
				msg_print("It glows black...");
				if (!get_aim_dir(&dir)) break;
				drain_life(dir, 120);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_VAMPIRE_1:
			{
				if (!doit) return "vampiric drain (3*50) every 400 turns";
				if (!get_aim_dir(&dir)) break;
				for (dummy = 0; dummy < 3; dummy++)
				{
					if (drain_life(dir, 50))
						hp_player(50);
				}

				o_ptr->timeout = 400;

				break;
			}

		case ACT_BO_MISS_2:
			{
				if (!doit) return "arrows (150) every 90+d90 turns";
				msg_print("It grows magical spikes...");
				if (!get_aim_dir(&dir)) break;
				fire_bolt(GF_ARROW, dir, 150);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BA_FIRE_2:
			{
				if (!doit) return "fire ball (120) every 225+d225 turns";
				msg_print("It glows deep red...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_FIRE, dir, 120, 3);

				o_ptr->timeout = rand_int(225) + 225;

				break;
			}

		case ACT_BA_COLD_3:
			{
				if (!doit) return "ball of cold (200) every 325+d325 turns";
				msg_print("It glows bright white...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_COLD, dir, 200, 3);

				o_ptr->timeout = rand_int(325) + 325;

				break;
			}

		case ACT_BA_ELEC_3:
			{
				if (!doit) return "Lightning Ball (250) every 425+d425 turns";
				msg_print("It glows deep blue...");
				if (!get_aim_dir(&dir)) break;
				fire_ball(GF_ELEC, dir, 250, 3);

				o_ptr->timeout = rand_int(425) + 425;

				break;
			}

		case ACT_WHIRLWIND:
			{
				int y = 0, x = 0;
				cave_type *c_ptr;
				monster_type *m_ptr;
				if (!doit) return "whirlwind attack every 250 turns";

				for (dir = 0; dir <= 9; dir++)
				{
					y = p_ptr->py + ddy[dir];
					x = p_ptr->px + ddx[dir];
					c_ptr = &cave[y][x];

					/* Get the monster */
					m_ptr = &m_list[c_ptr->m_idx];

					/* Hack -- attack monsters */
					if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x)))
					{
						py_attack(y, x, -1);
					}
				}

				o_ptr->timeout = 250;

				break;
			}

		case ACT_VAMPIRE_2:
			{
				if (!doit) return "vampiric drain (3*100) every 400 turns";
				if (!get_aim_dir(&dir)) break;
				for (dummy = 0; dummy < 3; dummy++)
				{
					if (drain_life(dir, 100))
						hp_player(100);
				}

				o_ptr->timeout = 400;

				break;
			}


		case ACT_CALL_CHAOS:
			{
				if (!doit) return "call chaos every 350 turns";
				msg_print("It glows in scintillating colours...");
				call_chaos();

				o_ptr->timeout = 350;

				break;
			}

		case ACT_ROCKET:
			{
				if (!doit) return "launch rocket (120+level) every 400 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You launch a rocket!");
				fire_ball(GF_ROCKET, dir, 120 + (plev), 2);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_DISP_EVIL:
			{
				if (!doit) return "dispel evil (level*5) every 300+d300 turns";
				msg_print("It floods the area with goodness...");
				dispel_evil(p_ptr->lev * 5);

				o_ptr->timeout = rand_int(300) + 300;

				break;
			}

		case ACT_DISP_GOOD:
			{
				if (!doit) return "dispel good (level*5) every 300+d300 turns";
				msg_print("It floods the area with evil...");
				dispel_good(p_ptr->lev * 5);

				o_ptr->timeout = rand_int(300) + 300;

				break;
			}

		case ACT_BA_MISS_3:
			{
				if (!doit) return "elemental breath (300) every 500 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe the elements.");
				fire_ball(GF_MISSILE, dir, 300, 4);

				o_ptr->timeout = 500;

				break;
			}

			/* Activate for other offensive action */

		case ACT_CONFUSE:
			{
				if (!doit) return "confuse monster every 15 turns";
				msg_print("It glows in scintillating colours...");
				if (!get_aim_dir(&dir)) break;
				confuse_monster(dir, 20);

				o_ptr->timeout = 15;

				break;
			}

		case ACT_SLEEP:
			{
				if (!doit) return "sleep nearby monsters every 55 turns";
				msg_print("It glows deep blue...");
				sleep_monsters_touch();

				o_ptr->timeout = 55;

				break;
			}

		case ACT_QUAKE:
			{
				if (!doit) return "earthquake (rad 10) every 50 turns";
				/* Prevent destruction of quest levels and town */
				if (!is_quest(dun_level) && dun_level)
				{
					earthquake(p_ptr->py, p_ptr->px, 10);
					o_ptr->timeout = 50;
				}

				break;
			}

		case ACT_TERROR:
			{
				if (!doit) return "terror every 3 * (level+10) turns";
				turn_monsters(40 + p_ptr->lev);

				o_ptr->timeout = 3 * (p_ptr->lev + 10);

				break;
			}

		case ACT_TELE_AWAY:
			{
				if (!doit) return "teleport away every 200 turns";
				if (!get_aim_dir(&dir)) break;
				(void)fire_beam(GF_AWAY_ALL, dir, plev);

				o_ptr->timeout = 200;

				break;
			}

		case ACT_BANISH_EVIL:
			{
				if (!doit) return "banish evil every 250+d250 turns";
				if (banish_evil(100))
				{
					msg_print("The power of the artifact banishes evil!");
				}

				o_ptr->timeout = 250 + randint(250);

				break;
			}

		case ACT_GENOCIDE:
			{
				if (!doit) return "genocide every 500 turns";
				msg_print("It glows deep blue...");
				(void)genocide(TRUE);

				o_ptr->timeout = 500;

				break;
			}

		case ACT_MASS_GENO:
			{
				if (!doit) return "mass genocide every 1000 turns";
				msg_print("It lets out a long, shrill note...");
				(void)mass_genocide(TRUE);

				o_ptr->timeout = 1000;

				break;
			}

			/* Activate for summoning / charming */

		case ACT_CHARM_ANIMAL:
			{
				if (!doit) return "charm animal every 300 turns";
				if (!get_aim_dir(&dir)) break;
				(void) charm_animal(dir, plev);

				o_ptr->timeout = 300;

				break;
			}

		case ACT_CHARM_UNDEAD:
			{
				if (!doit) return "enslave undead every 333 turns";
				if (!get_aim_dir(&dir)) break;
				(void)control_one_undead(dir, plev);

				o_ptr->timeout = 333;

				break;
			}

		case ACT_CHARM_OTHER:
			{
				if (!doit) return "charm monster every 400 turns";
				if (!get_aim_dir(&dir)) break;
				(void) charm_monster(dir, plev);

				o_ptr->timeout = 400;

				break;
			}

		case ACT_CHARM_ANIMALS:
			{
				if (!doit) return "animal friendship every 500 turns";
				(void) charm_animals(plev * 2);

				o_ptr->timeout = 500;

				break;
			}

		case ACT_CHARM_OTHERS:
			{
				if (!doit) return "mass charm every 750 turns";
				charm_monsters(plev * 2);

				o_ptr->timeout = 750;

				break;
			}

		case ACT_SUMMON_ANIMAL:
			{
				if (!doit) return "summon animal every 200+d300 turns";
				(void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE);

				o_ptr->timeout = 200 + randint(300);

				break;
			}

		case ACT_SUMMON_PHANTOM:
			{
				if (!doit) return "summon phantasmal servant every 200+d200 turns";
				msg_print("You summon a phantasmal servant.");
				(void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE);

				o_ptr->timeout = 200 + randint(200);

				break;
			}

		case ACT_SUMMON_ELEMENTAL:
			{
				if (!doit) return "summon elemental every 750 turns";
				if (randint(3) == 1)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL))
					{
						msg_print("An elemental materialises...");
						msg_print("You fail to control it!");
					}
				}
				else
				{
					if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
					                             SUMMON_ELEMENTAL, (bool_)(plev == 50 ? TRUE : FALSE)))
					{
						msg_print("An elemental materialises...");
						msg_print("It seems obedient to you.");
					}
				}

				o_ptr->timeout = 750;

				break;
			}

		case ACT_SUMMON_DEMON:
			{
				if (!doit) return "summon demon every 666+d333 turns";
				if (randint(3) == 1)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_DEMON))
					{
						msg_print("The area fills with a stench of sulphur and brimstone.");
						msg_print("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'");
					}
				}
				else
				{
					if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
					                             SUMMON_DEMON, (bool_)(plev == 50 ? TRUE : FALSE)))
					{
						msg_print("The area fills with a stench of sulphur and brimstone.");
						msg_print("'What is thy bidding... Master?'");
					}
				}

				o_ptr->timeout = 666 + randint(333);

				break;
			}

		case ACT_SUMMON_UNDEAD:
			{
				if (!doit) return "summon undead every 666+d333 turns";
				if (randint(3) == 1)
				{
					if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
					                    (plev > 47 ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD)))
					{
						msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
						msg_print("'The dead arise... to punish you for disturbing them!'");
					}
				}
				else
				{
					if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
					                             (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD),
					                             (bool_)(((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
					{
						msg_print("Cold winds begin to blow around you, carrying with them the stench of decay...");
						msg_print("Ancient, long-dead forms arise from the ground to serve you!");
					}
				}

				o_ptr->timeout = 666 + randint(333);

				break;
			}

			/* Activate for healing */

		case ACT_CURE_LW:
			{
				if (!doit) return format("cure light wounds every %d turns", (is_junkart ? 50 : 10));
				(void)set_afraid(0);
				(void)hp_player(30);

				o_ptr->timeout = 10;

				break;
			}

		case ACT_CURE_MW:
			{
				if (!doit) return format("cure serious wounds every %s turns", (is_junkart? "75" : "3+d3"));
				msg_print("It radiates deep purple...");
				hp_player(damroll(4, 8));
				(void)set_cut((p_ptr->cut / 2) - 50);

				o_ptr->timeout = rand_int(3) + 3;

				break;
			}

		case ACT_CURE_POISON:
			{
				if (!doit) return "remove fear and cure poison every 5 turns";
				msg_print("It glows deep blue...");
				(void)set_afraid(0);
				(void)set_poisoned(0);

				o_ptr->timeout = 5;

				break;
			}

		case ACT_REST_LIFE:
			{
				if (!doit) return "restore life levels every 450 turns";
				msg_print("It glows a deep red...");
				restore_level();

				o_ptr->timeout = 450;

				break;
			}

		case ACT_REST_ALL:
			{
				if (!doit) return format("restore stats and life levels every %d turns", (is_junkart ? 200 : 750));
				msg_print("It glows a deep green...");
				(void)do_res_stat(A_STR, TRUE);
				(void)do_res_stat(A_INT, TRUE);
				(void)do_res_stat(A_WIS, TRUE);
				(void)do_res_stat(A_DEX, TRUE);
				(void)do_res_stat(A_CON, TRUE);
				(void)do_res_stat(A_CHR, TRUE);
				(void)restore_level();

				o_ptr->timeout = 750;

				break;
			}

		case ACT_CURE_700:
			{
				if (!doit) return format("heal 700 hit points every %d turns", (is_junkart ? 100 : 250));
				msg_print("It glows deep blue...");
				msg_print("You feel a warm tingling inside...");
				(void)hp_player(700);
				(void)set_cut(0);

				o_ptr->timeout = 250;

				break;
			}

		case ACT_CURE_1000:
			{
				if (!doit) return "heal 1000 hit points every 888 turns";
				msg_print("It glows a bright white...");
				msg_print("You feel much better...");
				(void)hp_player(1000);
				(void)set_cut(0);

				o_ptr->timeout = 888;

				break;
			}

		case ACT_ESP:
			{
				if (!doit) return "temporary ESP (dur 25+d30) every 200 turns";
				(void)set_tim_esp(p_ptr->tim_esp + randint(30) + 25);

				o_ptr->timeout = 200;

				break;
			}

		case ACT_BERSERK:
			{
				if (!doit) return "heroism and berserk (dur 50+d50) every 100+d100 turns";
				(void)set_shero(p_ptr->shero + randint(50) + 50);
				(void)set_blessed(p_ptr->blessed + randint(50) + 50);

				o_ptr->timeout = 100 + randint(100);

				break;
			}

		case ACT_PROT_EVIL:
			{
				if (!doit) return "protection from evil (dur level*3 + d25) every 225+d225 turns";
				msg_print("It lets out a shrill wail...");
				k = 3 * p_ptr->lev;
				(void)set_protevil(p_ptr->protevil + randint(25) + k);

				o_ptr->timeout = rand_int(225) + 225;

				break;
			}

		case ACT_RESIST_ALL:
			{
				if (!doit) return "resist elements (dur 40+d40) every 200 turns";
				msg_print("It glows many colours...");
				(void)set_oppose_acid(p_ptr->oppose_acid + randint(40) + 40);
				(void)set_oppose_elec(p_ptr->oppose_elec + randint(40) + 40);
				(void)set_oppose_fire(p_ptr->oppose_fire + randint(40) + 40);
				(void)set_oppose_cold(p_ptr->oppose_cold + randint(40) + 40);
				(void)set_oppose_pois(p_ptr->oppose_pois + randint(40) + 40);

				o_ptr->timeout = 200;

				break;
			}

		case ACT_SPEED:
			{
				if (!doit) return "speed (dur 20+d20) every 250 turns";
				msg_print("It glows bright green...");
				if (!p_ptr->fast)
				{
					(void)set_fast(randint(20) + 20, 10);
				}
				else
				{
					(void)set_fast(p_ptr->fast + 5, 10);
				}

				o_ptr->timeout = 250;

				break;
			}

		case ACT_XTRA_SPEED:
			{
				if (!doit) return "speed (dur 75+d75) every 200+d200 turns";
				msg_print("It glows brightly...");
				if (!p_ptr->fast)
				{
					(void)set_fast(randint(75) + 75, 10);
				}
				else
				{
					(void)set_fast(p_ptr->fast + 5, 10);
				}

				o_ptr->timeout = rand_int(200) + 200;

				break;
			}

		case ACT_WRAITH:
			{
				if (!doit) return "wraith form (level/2 + d(level/2)) every 1000 turns";
				set_shadow(p_ptr->tim_wraith + randint(plev / 2) + (plev / 2));

				o_ptr->timeout = 1000;

				break;
			}

		case ACT_INVULN:
			{
				if (!doit) return "invulnerability (dur 8+d8) every 1000 turns";
				(void)set_invuln(p_ptr->invuln + randint(8) + 8);

				o_ptr->timeout = 1000;

				break;
			}

			/* Activate for general purpose effect (detection etc.) */

		case ACT_LIGHT:
			{
				if (!doit) return format("light area (dam 2d15) every %s turns", (is_junkart ? "100" : "10+d10"));
				msg_print("It wells with clear light...");
				lite_area(damroll(2, 15), 3);

				o_ptr->timeout = rand_int(10) + 10;

				break;
			}

		case ACT_MAP_LIGHT:
			{
				if (!doit) return "light (dam 2d15) & map area every 50+d50 turns";
				msg_print("It shines brightly...");
				map_area();
				lite_area(damroll(2, 15), 3);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}

		case ACT_DETECT_ALL:
			{
				if (!doit) return "detection every 55+d55 turns";
				msg_print("It glows bright white...");
				msg_print("An image forms in your mind...");
				detect_all(DEFAULT_RADIUS);

				o_ptr->timeout = rand_int(55) + 55;

				break;
			}

		case ACT_DETECT_XTRA:
			{
				if (!doit) return "detection, probing and identify true every 1000 turns";
				msg_print("It glows brightly...");
				detect_all(DEFAULT_RADIUS);
				probing();
				identify_fully();

				o_ptr->timeout = 1000;

				break;
			}

		case ACT_ID_FULL:
			{
				if (!doit) return "identify true every 750 turns";
				msg_print("It glows yellow...");
				identify_fully();

				o_ptr->timeout = 750;

				break;
			}

		case ACT_ID_PLAIN:
			{
				if (!doit) return "identify spell every 10 turns";
				if (!ident_spell()) break;

				o_ptr->timeout = 10;

				break;
			}

		case ACT_RUNE_EXPLO:
			{
				if (!doit) return "explosive rune every 200 turns";
				msg_print("It glows bright red...");
				explosive_rune();

				o_ptr->timeout = 200;

				break;
			}

		case ACT_RUNE_PROT:
			{
				if (!doit) return "rune of protection every 400 turns";
				msg_print("It glows light blue...");
				warding_glyph();

				o_ptr->timeout = 400;

				break;
			}

		case ACT_SATIATE:
			{
				if (!doit) return "satisfy hunger every 200 turns";
				(void)set_food(PY_FOOD_MAX - 1);

				o_ptr->timeout = 200;

				break;
			}

		case ACT_DEST_DOOR:
			{
				if (!doit) return "destroy doors and traps every 10 turns";
				msg_print("It glows bright red...");
				destroy_doors_touch();

				o_ptr->timeout = 10;

				break;
			}

		case ACT_STONE_MUD:
			{
				if (!doit) return "stone to mud every 5 turns";
				msg_print("It pulsates...");
				if (!get_aim_dir(&dir)) break;
				wall_to_mud(dir);

				o_ptr->timeout = 5;

				break;
			}

		case ACT_RECHARGE:
			{
				if (!doit) return "recharging every 70 turns";
				recharge(60);

				o_ptr->timeout = 70;

				break;
			}

		case ACT_ALCHEMY:
			{
				if (!doit) return "alchemy every 500 turns";
				msg_print("It glows bright yellow...");
				(void) alchemy();

				o_ptr->timeout = 500;

				break;
			}

		case ACT_DIM_DOOR:
			{
				if (!doit) return "dimension door every 100 turns";
				if (dungeon_flags2 & DF2_NO_TELEPORT)
				{
					msg_print("Not on special levels!");
					break;
				}

				msg_print("You open a Void Jumpgate. Choose a destination.");
				if (!tgt_pt(&ii, &ij)) break;

				p_ptr->energy -= 60 - plev;

				if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
				                (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2) ||
				                (!rand_int(plev * plev / 2)))
				{
					msg_print("You fail to exit the void correctly!");
					p_ptr->energy -= 100;
					get_pos_player(10, &ij, &ii);
				}

				cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
				cave_set_feat(ij, ii, FEAT_BETWEEN);
				cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
				cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);

				o_ptr->timeout = 100;

				break;
			}

		case ACT_TELEPORT:
			{
				if (!doit) return format("teleport (range 100) every %d turns", (is_junkart? 100 : 45));
				msg_print("It twists space around you...");
				teleport_player(100);

				o_ptr->timeout = 45;

				break;
			}

		case ACT_RECALL:
			{
				if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
				{
					if (!doit) return "word of recall every 200 turns";
					msg_print("It glows soft white...");
					recall_player(20,15);

					o_ptr->timeout = 200;
				}

				break;
			}

		case ACT_DEATH:
			{
				if (!doit) return "death";
				take_hit(5000, "activating a death spell");

				/* Timeout is set before return */

				break;
			}

		case ACT_RUINATION:
			{
				if (!doit) return "Ruination";
				msg_print("Your nerves and muscles feel weak and lifeless!");

				take_hit(damroll(10, 10), "activating Ruination");
				(void)dec_stat(A_DEX, 25, TRUE);
				(void)dec_stat(A_WIS, 25, TRUE);
				(void)dec_stat(A_CON, 25, TRUE);
				(void)dec_stat(A_STR, 25, TRUE);
				(void)dec_stat(A_CHR, 25, TRUE);
				(void)dec_stat(A_INT, 25, TRUE);

				/* Timeout is set before return */

				break;
			}

		case ACT_DESTRUC:
			{
				if (!doit) return "Destruction every 100 turns";
				earthquake(p_ptr->py, p_ptr->px, 12);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNINT:
			{
				if (!doit) return "decreasing Intelligence";
				(void)dec_stat(A_INT, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNSTR:
			{
				if (!doit) return "decreasing Strength";
				(void)dec_stat(A_STR, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNCON:
			{
				if (!doit) return "decreasing Constitution";
				(void)dec_stat(A_CON, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNCHR:
			{
				if (!doit) return "decreasing Charisma";
				(void)dec_stat(A_CHR, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNDEX:
			{
				if (!doit) return "decreasing Dexterity";
				(void)dec_stat(A_DEX, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_UNWIS:
			{
				if (!doit) return "decreasing Wisdom";
				(void)dec_stat(A_WIS, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_STATLOSS:
			{
				if (!doit) return "stat loss";
				(void)dec_stat(A_STR, 15, FALSE);
				(void)dec_stat(A_INT, 15, FALSE);
				(void)dec_stat(A_WIS, 15, FALSE);
				(void)dec_stat(A_DEX, 15, FALSE);
				(void)dec_stat(A_CON, 15, FALSE);
				(void)dec_stat(A_CHR, 15, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_HISTATLOSS:
			{
				if (!doit) return "high stat loss";
				(void)dec_stat(A_STR, 25, FALSE);
				(void)dec_stat(A_INT, 25, FALSE);
				(void)dec_stat(A_WIS, 25, FALSE);
				(void)dec_stat(A_DEX, 25, FALSE);
				(void)dec_stat(A_CON, 25, FALSE);
				(void)dec_stat(A_CHR, 25, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_EXPLOSS:
			{
				if (!doit) return "experience loss";
				lose_exp(p_ptr->exp / 20);

				/* Timeout is set before return */

				break;
			}

		case ACT_HIEXPLOSS:
			{
				if (!doit) return "high experience loss";
				lose_exp(p_ptr->exp / 10);

				/* Timeout is set before return */

				break;
			}

		case ACT_SUMMON_MONST:
			{
				if (!doit) return "summon monster";
				summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0);

				/* Timeout is set before return */

				break;
			}

		case ACT_PARALYZE:
			{
				if (!doit) return "paralyze";
				set_paralyzed(p_ptr->paralyzed + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_HALLU:
			{
				if (!doit) return "hallucination every 10 turns";
				set_image(p_ptr->image + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_POISON:
			{
				if (!doit) return "poison";
				set_poisoned(p_ptr->poisoned + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_HUNGER:
			{
				if (!doit) return "create hunger";
				(void)set_food(PY_FOOD_WEAK);

				/* Timeout is set before return */

				break;
			}

		case ACT_STUN:
			{
				if (!doit) return "stun";
				set_stun(p_ptr->stun + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_CUTS:
			{
				if (!doit) return "cuts";
				set_cut(p_ptr->cut + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_PARANO:
			{
				if (!doit) return "confusion";
				set_confused(p_ptr->confused + 30 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_CONFUSION:
			{
				if (!doit) return "confusion";
				set_confused(p_ptr->confused + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_BLIND:
			{
				if (!doit) return "blindness";
				set_blind(p_ptr->blind + 20 + randint(10));

				/* Timeout is set before return */

				break;
			}

		case ACT_PET_SUMMON:
			{
				if (!doit) return "summon pet every 101 turns";
				summon_specific_friendly(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0, FALSE);

				/* Timeout is set before return */
				/*FINDME*/

				break;
			}

		case ACT_CURE_PARA:
			{
				if (!doit) return "cure confusion every 500 turns";
				set_confused(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_HALLU:
			{
				if (!doit) return "cure hallucination every 100 turns";
				set_image(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_POIS:
			{
				if (!doit) return "cure poison every 100 turns";
				set_poisoned(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_HUNGER:
			{
				if (!doit) return "satisfy hunger every 100 turns";
				(void)set_food(PY_FOOD_MAX - 1);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_STUN:
			{
				if (!doit) return "cure stun every 100 turns";
				set_stun(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_CUTS:
			{
				if (!doit) return "cure cuts every 100 turns";
				set_cut(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_FEAR:
			{
				if (!doit) return "cure fear every 100 turns";
				set_afraid(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_CONF:
			{
				if (!doit) return "cure confusion every 100 turns";
				set_confused(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_BLIND:
			{
				if (!doit) return "cure blindness every 100 turns";
				set_blind(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_CURING:
			{
				if (!doit) return "curing every 110 turns";
				set_blind(0);
				set_poisoned(0);
				set_confused(0);
				set_stun(0);
				set_cut(0);
				set_image(0);

				/* Timeout is set before return */

				break;
			}

		case ACT_DARKNESS:
			{
				if (!doit) return "darkness";
				unlite_area(damroll(2, 10), 10);

				/* Timeout is set before return */

				break;
			}

		case ACT_LEV_TELE:
			{
				if (!doit) return "teleport level every 50 turns";
				teleport_player_level();

				/* Timeout is set before return */

				break;
			}

		case ACT_ACQUIREMENT:
			{
				if (!doit) return "acquirement every 3000 turns";
				acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE);

				/* Timeout is set before return */

				break;
			}

		case ACT_WEIRD:
			{
				if (!doit) return "something weird every 5 turns";
				/* It doesn't do anything */

				/* Timeout is set before return */

				break;
			}

		case ACT_AGGRAVATE:
			{
				if (!doit) return "aggravate";
				aggravate_monsters(1);

				/* Timeout is set before return */

				break;
			}

		case ACT_MUT:
			{
				if (!doit) return "gain corruption every 10 turns";
				gain_random_corruption(0);
				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_INSANITY:
			{
				if (!doit) return "cure insanity every 200 turns";
				heal_insanity(damroll(10, 10));

				/* Timeout is set before return */

				break;
			}

		case ACT_CURE_MUT:
			{
				msg_print("Ahah, you wish.");
				/* Timeout is set before return */

				break;
			}

		case ACT_LIGHT_ABSORBTION:
			{
				int y, x, light = 0, dir;
				cave_type *c_ptr;

				if (!doit) return "light absorption every 80 turns";

				for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++)
				{
					for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++)
					{
						if (!in_bounds(y, x)) continue;

						c_ptr = &cave[y][x];

						if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue;

						if (c_ptr->info & CAVE_GLOW)
						{
							light++;

							/* No longer in the array */
							c_ptr->info &= ~(CAVE_TEMP);

							/* Darken the grid */
							c_ptr->info &= ~(CAVE_GLOW);

							/* Hack -- Forget "boring" grids */
							if (cave_plain_floor_grid(c_ptr) &&
							                !(c_ptr->info & (CAVE_TRDT)))
							{
								/* Forget the grid */
								c_ptr->info &= ~(CAVE_MARK);

								/* Notice */
								note_spot(y, x);
							}

							/* Process affected monsters */
							if (c_ptr->m_idx)
							{
								/* Update the monster */
								update_mon(c_ptr->m_idx, FALSE);
							}

							/* Redraw */
							lite_spot(y, x);
						}
					}
				}

				if (!get_aim_dir(&dir)) return (FALSE);

				msg_print("The light around you is absorbed... "
				          "and released in a powerful bolt!");
				fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev));

				/* Timeout is set before return */

				break;
			}
			/* Horns of DragonKind (Note that these are new egos)*/
		case ACT_BA_FIRE_H:
			{
				if (!doit) return "large fire ball (300) every 100 turns";
				fire_ball(GF_FIRE, 5, 300, 7);

				o_ptr->timeout = 100;

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

				break;
			}
		case ACT_BA_COLD_H:
			{
				if (!doit) return "large cold ball (300) every 100 turns";
				fire_ball(GF_COLD, 5, 300, 7);

				o_ptr->timeout = 100;

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

				break;
			}
		case ACT_BA_ELEC_H:
			{
				if (!doit) return "large lightning ball (300) every 100 turns";
				fire_ball(GF_ELEC, 5, 300, 7);

				o_ptr->timeout = 100;

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

				break;
			}
		case ACT_BA_ACID_H:
			{
				if (!doit) return "large acid ball (300) every 100 turns";
				fire_ball(GF_ACID, 5, 300, 7);

				o_ptr->timeout = 100;

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

				break;
			}

		case ACT_SPIN:
			{
				if (!doit) return "spinning around every 50+d25 turns";
				do_spin();

				o_ptr->timeout = 50 + randint(25);

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

				/* Done */
				break;
			}
		case ACT_NOLDOR:
			{
				if (!doit) return "detect treasure every 10+d20 turns";
				detect_treasure(DEFAULT_RADIUS);

				o_ptr->timeout = 10 + randint(20);

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

				/* Done */
				break;
			}
		case ACT_SPECTRAL:
			{
				if (!doit) return "wraith-form every 50+d50 turns";
				if (!p_ptr->wraith_form)
				{
					set_shadow(20 + randint(20));
				}
				else
				{
					set_shadow(p_ptr->tim_wraith + randint(20));
				}

				o_ptr->timeout = 50 + randint(50);

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

				/* Done */
				break;
			}
		case ACT_JUMP:
			{
				if (!doit) return "phasing every 10+d10 turns";
				teleport_player(10);
				o_ptr->timeout = 10 + randint(10);

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

				/* Done */
				break;
			}

		case ACT_DEST_TELE:
			{
				if (!doit) return "teleportation and destruction of the ring";
				if (!item)
				{
					msg_print("You can't activate this when it's there!");
				}
				if (get_check("This will destroy the ring. Do you wish to continue? "))
				{
					msg_print("The ring explodes into a space distortion.");
					teleport_player(200);

					/* It explodes, doesn't it ? */
					take_hit(damroll(2, 10), "an exploding ring");
					if ( item > 0)
					{
						inven_item_increase(item, -255);
						inven_item_optimize(item);
					}
					else
					{
						floor_item_increase( -item, -255);
						floor_item_optimize( -item);
					}
				}

				break;
			}
			/*amulet of serpents dam 100, rad 2 timeout 40+d60 */
		case ACT_BA_POIS_4:
			{
				if (!doit) return "venom breathing every 40+d60 turns";
				/* Get a direction for breathing (or abort) */
				if (!get_aim_dir(&dir)) break;

				msg_print("You breathe venom...");
				fire_ball(GF_POIS, dir, 100, 2);

				o_ptr->timeout = rand_int(60) + 40;

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

				/* Done */
				break;
			}
			/*rings of X 50,50+d50 dur 20+d20 */
		case ACT_BA_COLD_4:
			{
				if (!doit) return "ball of cold and resist cold every 50+d50 turns";
				/* Get a direction for breathing (or abort) */
				if (!get_aim_dir(&dir)) break;

				fire_ball(GF_COLD, dir, 50, 2);
				(void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}

		case ACT_BA_FIRE_4:
			{
				if (!doit) return "ball of fire and resist fire every 50+d50 turns";
				/* Get a direction for breathing (or abort) */
				if (!get_aim_dir(&dir)) break;

				fire_ball(GF_FIRE, dir, 50, 2);
				(void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}
		case ACT_BA_ACID_4:
			{
				if (!doit) return "ball of acid and resist acid every 50+d50 turns";
				/* Get a direction for breathing (or abort) */
				if (!get_aim_dir(&dir)) break;

				fire_ball(GF_ACID, dir, 50, 2);
				(void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}

		case ACT_BA_ELEC_4:
			{
				if (!doit) return "ball of lightning and resist lightning every 50+d50 turns";
				/* Get a direction for breathing (or abort) */
				if (!get_aim_dir(&dir)) break;

				fire_ball(GF_ELEC, dir, 50, 2);
				(void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);

				o_ptr->timeout = rand_int(50) + 50;

				break;
			}

		case ACT_BR_ELEC:
			{
				if (!doit) return "breathe lightning (100) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe lightning.");
				fire_ball(GF_ELEC, dir, 100, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_COLD:
			{
				if (!doit) return "breathe frost (110) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe frost.");
				fire_ball(GF_COLD, dir, 110, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_FIRE:
			{
				if (!doit) return "breathe fire (200) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe fire.");
				fire_ball(GF_FIRE, dir, 200, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_ACID:
			{
				if (!doit) return "breathe acid (130) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe acid.");
				fire_ball(GF_ACID, dir, 130, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_POIS:
			{
				if (!doit) return "breathe poison gas (150) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe poison gas.");
				fire_ball(GF_POIS, dir, 150, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_MANY:
			{
				if (!doit) return "breathe multi-hued (250) every 60+d60 turns";
				if (!get_aim_dir(&dir)) break;
				chance = rand_int(5);
				msg_format("You breathe %s.",
				           ((chance == 1) ? "lightning" :
				            ((chance == 2) ? "frost" :
				             ((chance == 3) ? "acid" :
				              ((chance == 4) ? "poison gas" : "fire")))));
				fire_ball(((chance == 1) ? GF_ELEC :
				           ((chance == 2) ? GF_COLD :
				            ((chance == 3) ? GF_ACID :
				             ((chance == 4) ? GF_POIS : GF_FIRE)))),
				          dir, 250, 2);

				o_ptr->timeout = rand_int(60) + 60;

				break;
			}

		case ACT_BR_CONF:
			{
				if (!doit) return "breathe confusion (120) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe confusion.");
				fire_ball(GF_CONFUSION, dir, 120, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_SOUND:
			{
				if (!doit) return "breathe sound (130) every 90+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe sound.");
				fire_ball(GF_SOUND, dir, 130, 2);

				o_ptr->timeout = rand_int(90) + 90;

				break;
			}

		case ACT_BR_CHAOS:
			{
				if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns";
				if (!get_aim_dir(&dir)) break;
				chance = rand_int(2);
				msg_format("You breathe %s.",
				           ((chance == 1 ? "chaos" : "disenchantment")));
				fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT),
				          dir, 220, 2);

				o_ptr->timeout = rand_int(90) + 60;

				break;
			}

		case ACT_BR_SHARD:
			{
				if (!doit) return "breathe sound/shards (230) every 60+d90 turns";
				if (!get_aim_dir(&dir)) break;
				chance = rand_int(2);
				msg_format("You breathe %s.",
				           ((chance == 1 ? "sound" : "shards")));
				fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS),
				          dir, 230, 2);

				o_ptr->timeout = rand_int(90) + 60;

				break;
			}

		case ACT_BR_BALANCE:
			{
				if (!doit) return "breathe balance (250) every 60+d90 turns";
				if (!get_aim_dir(&dir)) break;
				chance = rand_int(4);
				msg_format("You breathe %s.",
				           ((chance == 1) ? "chaos" :
				            ((chance == 2) ? "disenchantment" :
				             ((chance == 3) ? "sound" : "shards"))));
				fire_ball(((chance == 1) ? GF_CHAOS :
				           ((chance == 2) ? GF_DISENCHANT :
				            ((chance == 3) ? GF_SOUND : GF_SHARDS))),
				          dir, 250, 2);

				o_ptr->timeout = rand_int(90) + 60;

				break;
			}

		case ACT_BR_LIGHT:
			{
				if (!doit) return "breathe light/darkness (200) every 60+d90 turns";
				if (!get_aim_dir(&dir)) break;
				chance = rand_int(2);
				msg_format("You breathe %s.",
				           ((chance == 0 ? "light" : "darkness")));
				fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2);

				o_ptr->timeout = rand_int(90) + 60;

				break;
			}
		case ACT_BR_POWER:
			{
				if (!doit) return "breathe the elements (300) every 60+d90 turns";
				if (!get_aim_dir(&dir)) break;
				msg_print("You breathe the elements.");
				fire_ball(GF_MISSILE, dir, 300, 3);

				o_ptr->timeout = rand_int(90) + 60;

				break;
			}
		case ACT_GROW_MOLD:
			{
				if (!doit) return "grow mushrooms every 50+d50 turns";
				msg_print("You twirl and spores fly everywhere!");
				for (i = 0; i < 8; i++)
					summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);

				o_ptr->timeout = randint(50) + 50;

				break;
			}
		case ACT_MUSIC:
			/*
			 fall through to unknown, as music should be
			 handled by calling procedure.
			 */

		default:
			{
				msg_format("Unknown activation effect: %d.", spell);
				if ( !doit ) return "Unknown Activation";
				return NULL;
			}
		}
	}

	/* Set timeout for junkarts
	 * Note that I still need to set the timeouts for other
	 * (non-random) artifacts above 
	 */
	if (is_junkart && doit)
		o_ptr->timeout = activation_info[o_ptr->pval2].cost / 10;

	return NULL;
}


static bool_ activate_spell(object_type * o_ptr, byte choice)
{
	int mana = 0, gf = 0, mod = 0;

	rune_spell s_ptr;


	if (choice == 1)
	{
		gf = o_ptr->pval & 0xFFFF;
		mod = o_ptr->pval3 & 0xFFFF;
		mana = o_ptr->pval2 & 0xFF;
	}
	else if (choice == 2)
	{
		gf = o_ptr->pval >> 16;
		mod = o_ptr->pval3 >> 16;
		mana = o_ptr->pval2 >> 8;
	}

	s_ptr.type = gf;
	s_ptr.rune2 = 1 << mod;
	s_ptr.mana = mana;

	/* Execute */
	rune_exec(&s_ptr, 0);

	if (choice == 1) o_ptr->timeout = mana * 5;
	if (choice == 2) o_ptr->xtra2 = mana * 5;

	return (TRUE);
}
