/* テスト */
/* 
 * Debug Status: Not checked.
 * - Fixed __T on this page.
 */
/*
 * File: player/timed.c
 * Purpose: Timed effects handling
 *
 * Copyright (c) 1997 Ben Harrison
 * Copyright (c) 2007 A Sidwell <andi@takkaria.org>
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */
extern "C"
{
#include "angband.h"
}
extern void msg_print(const _TCHAR *msg);

/*
 * The "stun" and "cut" statuses need to be handled by special functions of
 * their own, as they are more complex than the ones handled by the generic
 * code.
 */
static bool set_stun(int v);
static bool set_cut(int v);


typedef struct
{
  const _TCHAR *on_begin;
  const _TCHAR *on_end;
  const _TCHAR *on_increase;
  const _TCHAR *on_decrease;
  u32b flag_redraw, flag_update;
  int msg;
} timed_effect;

static timed_effect effects[] =
{
	{ __T("You feel yourself moving faster!"), __T("You feel yourself slow down."),
			NULL, NULL,
			0, PU_BONUS, MSG_SPEED },
	{ __T("You feel yourself moving slower!"), __T("You feel yourself speed up."),
			NULL, NULL,
			0, PU_BONUS, MSG_SLOW },
	{ __T("You are blind."), __T("You blink and your eyes clear."),
			NULL, NULL,
			PR_MAP, PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS, MSG_BLIND },
	{ __T("You are paralysed!"), __T("You can move again."),
			__T("You are more paralysed!"), __T("You are less paralysed."),
			0, 0, MSG_PARALYZED },
	{ __T("You are confused!"), __T("You are no longer confused."),
			__T("You are more confused!"), __T("You feel a little less confused."),
			0, 0, MSG_CONFUSED },
	{ __T("You are terrified!"), __T("You feel bolder now."),
			__T("You are more scared!"), __T("You feel a little less scared."),
			0, PU_BONUS, MSG_AFRAID },
	{ __T("You feel drugged!"), __T("You can see clearly again."),
			__T("You feel more drugged!"), __T("You feel less drugged."),
			PR_MAP, 0, MSG_DRUGGED },
	{ __T("You are poisoned!"), __T("You are no longer poisoned."),
			__T("You are more poisoned!"), __T("You are less poisoned."),
			0, 0, MSG_POISONED },
	{ NULL, NULL, NULL, NULL, 0, 0, 0 },  /* TMD_CUT -- handled seperately */
	{ NULL, NULL, NULL, NULL, 0, 0, 0 },  /* TMD_STUN -- handled seperately */
	{ __T("You feel safe from evil!"), __T("You no longer feel safe from evil."),
			__T("You feel even safer from evil!"), __T("You feel less safe from evil."),
			0, 0, MSG_PROT_EVIL },
	{ __T("You feel invulnerable!"), __T("You feel vulnerable once more."),
			NULL, NULL,
			0, PU_BONUS, MSG_INVULN },
	{ __T("You feel like a hero!"), __T("You no longer feel heroic."),
			__T("You feel more like a hero!"), __T("You feel less heroic."),
			0, PU_BONUS, MSG_HERO },
	{ __T("You feel like a killing machine!"), __T("You no longer feel berserk."),
			__T("You feel even more berserk!"), __T("You feel less berserk."),
			0, PU_BONUS, MSG_BERSERK },
	{ __T("A mystic shield forms around your body!"), __T("Your mystic shield crumbles away."),
			__T("The mystic shield strengthens."), __T("The mystic shield weakens."),
			0, PU_BONUS, MSG_SHIELD },
	{ __T("You feel righteous!"), __T("The prayer has expired."),
			__T("You feel more righteous!"), __T("You feel less righteous."),
			0, PU_BONUS, MSG_BLESSED },
	{ __T("Your eyes feel very sensitive!"), __T("Your no longer feel so sensitive."),
			__T("Your eyes feel more sensitive!"), __T("Your eyes feel less sensitive."),
			0, (PU_BONUS | PU_MONSTERS), MSG_SEE_INVIS },
	{ __T("Your eyes begin to tingle!"), __T("Your eyes stop tingling."),
			__T("Your eyes' tingling intensifies."), __T("Your eyes tingle less."),
			0, (PU_BONUS | PU_MONSTERS), MSG_INFRARED },
	{ __T("You feel resistant to acid!"), __T("You are no longer resistant to acid."),
			__T("You feel more resistant to acid!"), __T("You feel less resistant to acid."),
			PR_STATUS, 0, MSG_RES_ACID },
	{ __T("You feel resistant to electricity!"), __T("You are no longer resistant to electricity."),
			__T("You feel more resistant to electricity!"), __T("You feel less resistant to electricity."),
			PR_STATUS, 0, MSG_RES_ELEC },
	{ __T("You feel resistant to fire!"), __T("You are no longer resistant to fire."),
			__T("You feel more resistant to fire!"), __T("You feel less resistant to fire."),
			PR_STATUS, 0, MSG_RES_FIRE },
	{ __T("You feel resistant to cold!"), __T("You are no longer resistant to cold."),
			__T("You feel more resistant to cold!"), __T("You feel less resistant to cold."),
			PR_STATUS, 0, MSG_RES_COLD },
	{ __T("You feel resistant to poison!"), __T("You are no longer resistant to poison."),
			__T("You feel more resistant to poison!"), __T("You feel less resistant to poison."),
			0, 0, MSG_RES_POIS },
	{ __T("You feel resistant to confusion!"), __T("You are no longer resistant to confusion."),
			__T("You feel more resistant to confusion!"), __T("You feel less resistant to confusion."),
			PR_STATUS, PU_BONUS, 0 },
	{ __T("You feel your memories fade."), __T("Your memories come flooding back."),
			NULL, NULL,
			0, 0, MSG_GENERIC },
	{ __T("Your mind expands."), __T("Your horizons are once more limited."),
			__T("Your mind expands further."), NULL,
			0, PU_BONUS, MSG_GENERIC },
	{ __T("Your skin turns to stone."), __T("A fleshy shade returns to your skin."),
			NULL, NULL,
			0, PU_BONUS, MSG_GENERIC },
	{ __T("You feel the need to run away, and fast!"), __T("The urge to run dissipates."),
			NULL, NULL,
			0, PU_BONUS, MSG_AFRAID },
	{ __T("You start sprinting."), __T("You suddenly stop sprinting."),
			NULL, NULL,
			0, PU_BONUS, MSG_SPEED },
};

/*
 * Set a timed event (except timed resists, cutting and stunning).
 */
bool set_timed(int idx, int v, bool notify)
{
	timed_effect *effect;

	/* Hack -- Force good values */
	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
	if ((idx < 0) || (idx > TMD_MAX)) return FALSE;

	/* No change */
	if (p_ptr->timed[idx] == v) return FALSE;

	/* Hack -- call other functions */
	if (idx == TMD_STUN) return set_stun(v);
	else if (idx == TMD_CUT) return set_cut(v);

	/* Don't mention some effects. */
	if (idx == TMD_OPP_ACID && p_ptr->state.immune_acid) notify = FALSE;
	else if (idx == TMD_OPP_ELEC && p_ptr->state.immune_elec) notify = FALSE;
	else if (idx == TMD_OPP_FIRE && p_ptr->state.immune_fire) notify = FALSE;
	else if (idx == TMD_OPP_COLD && p_ptr->state.immune_cold) notify = FALSE;
	else if (idx == TMD_OPP_CONF && p_ptr->state.resist_confu) notify = FALSE;

	/* Find the effect */
	effect = &effects[idx];

	/* Turning off, always mention */
	if (v == 0)
	{
		message(MSG_RECOVER, 0, effect->on_end);
		notify = TRUE;
	}
	/* Turning on, always mention */
	else if (p_ptr->timed[idx] == 0)
	{
		message(effect->msg, 0, effect->on_begin);
		notify = TRUE;
	}
	else if (notify)
	{
		/* Decrementing */
		if (p_ptr->timed[idx] > v && effect->on_decrease)
			message(effect->msg, 0, effect->on_decrease);

		/* Incrementing */
		else if (v > p_ptr->timed[idx] && effect->on_decrease)
			message(effect->msg, 0, effect->on_increase);
	}
	/* Use the value */
	p_ptr->timed[idx] = v;

	/* Sort out the sprint effect */
	if (idx == TMD_SPRINT && v == 0)
		inc_timed(TMD_SLOW, 100, TRUE);

	/* Nothing to notice */
	if (!notify) return FALSE;

	/* Disturb */
	if (OPT(disturb_state)) disturb(0, 0);

	/* Update the visuals, as appropriate. */
	p_ptr->update |= effect->flag_update;
	p_ptr->redraw |= (PR_STATUS | effect->flag_redraw);

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return TRUE;
}

/**
 * Increase the timed effect `idx` by `v`.  Mention this if `notify` is TRUE.
 */
bool inc_timed(int idx, int v, bool notify)
{
	/* Check we have a valid effect */
	if ((idx < 0) || (idx > TMD_MAX)) return FALSE;

	/* Set v */
	v = v + p_ptr->timed[idx];

	return set_timed(idx, v, notify);
}

/**
 * Decrease the timed effect `idx` by `v`.  Mention this if `notify` is TRUE.
 */
bool dec_timed(int idx, int v, bool notify)
{
	/* Check we have a valid effect */
	if ((idx < 0) || (idx > TMD_MAX)) return FALSE;

	/* Set v */
	v = p_ptr->timed[idx] - v;

	return set_timed(idx, v, notify);
}

/**
 * Clear the timed effect `idx`.  Mention this if `notify` is TRUE.
 */
bool clear_timed(int idx, bool notify)
{
	return set_timed(idx, 0, notify);
}

/*
 * Set "p_ptr->timed[TMD_STUN]", notice observable changes
 *
 * Note the special code to only notice "range" changes.
 */
static bool set_stun(int v)
{
	int old_aux, new_aux;

	bool notice = FALSE;

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

	/* Knocked out */
	if (p_ptr->timed[TMD_STUN] > 100)
	{
		old_aux = 3;
	}
	/* Heavy stun */
	else if (p_ptr->timed[TMD_STUN] > 50)
	{
		old_aux = 2;
	}
	/* Stun */
	else if (p_ptr->timed[TMD_STUN] > 0)
	{
		old_aux = 1;
	}
	else /* None */
	{
		old_aux = 0;
	}
	/* Knocked out */
	if (v > 100)
	{
		new_aux = 3;
	}
	/* Heavy stun */
	else if (v > 50)
	{
		new_aux = 2;
	}
	/* Stun */
	else if (v > 0)
	{
		new_aux = 1;
	}
	/* None */
	else
	{
		new_aux = 0;
	}
	/* Increase cut */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Stun */
			case 1:
			{
				message(MSG_STUN, 0, __T("You have been stunned."));
				break;
			}
			/* Heavy stun */
			case 2:
			{
				message(MSG_STUN, 0, __T("You have been heavily stunned."));
				break;
			}
			/* Knocked out */
			case 3:
			{
				message(MSG_STUN, 0, __T("You have been knocked out."));
				break;
			}
		}
		/* Notice */
		notice = TRUE;
	}
	/* Decrease cut */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* None */
			case 0:
			{
				message(MSG_RECOVER, 0, __T("You are no longer stunned."));
				if (OPT(disturb_state)) disturb(0, 0);
				break;
			}
		}
		/* Notice */
		notice = TRUE;
	}
	/* Use the value */
	p_ptr->timed[TMD_STUN] = v;

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

	/* Disturb */
	if (OPT(disturb_state)) disturb(0, 0);

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

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

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return (TRUE);
}

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

	bool notice = FALSE;

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

	/* Mortal wound */
	if (p_ptr->timed[TMD_CUT] > 1000)
	{
		old_aux = 7;
	}
	/* Deep gash */
	else if (p_ptr->timed[TMD_CUT] > 200)
	{
		old_aux = 6;
	}
	/* Severe cut */
	else if (p_ptr->timed[TMD_CUT] > 100)
	{
		old_aux = 5;
	}
	/* Nasty cut */
	else if (p_ptr->timed[TMD_CUT] > 50)
	{
		old_aux = 4;
	}
	/* Bad cut */
	else if (p_ptr->timed[TMD_CUT] > 25)
	{
		old_aux = 3;
	}
	/* Light cut */
	else if (p_ptr->timed[TMD_CUT] > 10)
	{
		old_aux = 2;
	}
	/* Graze */
	else if (p_ptr->timed[TMD_CUT] > 0)
	{
		old_aux = 1;
	}
	else /* None */
	{
		old_aux = 0;
	}
	/* Mortal wound */
	if (v > 1000)
	{
		new_aux = 7;
	}
	/* Deep gash */
	else if (v > 200)
	{
		new_aux = 6;
	}
	/* Severe cut */
	else if (v > 100)
	{
		new_aux = 5;
	}
	/* Nasty cut */
	else if (v > 50)
	{
		new_aux = 4;
	}
	/* Bad cut */
	else if (v > 25)
	{
		new_aux = 3;
	}
	/* Light cut */
	else if (v > 10)
	{
		new_aux = 2;
	}
	/* Graze */
	else if (v > 0)
	{
		new_aux = 1;
	}
	else /* None */
	{
		new_aux = 0;
	}
	/* Increase cut */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Graze */
			case 1:
			{
				message(MSG_CUT, 0, __T("You have been given a graze."));
				break;
			}
			/* Light cut */
			case 2:
			{
				message(MSG_CUT, 0, __T("You have been given a light cut."));
				break;
			}
			/* Bad cut */
			case 3:
			{
				message(MSG_CUT, 0, __T("You have been given a bad cut."));
				break;
			}
			/* Nasty cut */
			case 4:
			{
				message(MSG_CUT, 0, __T("You have been given a nasty cut."));
				break;
			}
			/* Severe cut */
			case 5:
			{
				message(MSG_CUT, 0, __T("You have been given a severe cut."));
				break;
			}
			/* Deep gash */
			case 6:
			{
				message(MSG_CUT, 0, __T("You have been given a deep gash."));
				break;
			}
			/* Mortal wound */
			case 7:
			{
				message(MSG_CUT, 0, __T("You have been given a mortal wound."));
				break;
			}
		}
		/* Notice */
		notice = TRUE;
	}
	/* Decrease cut */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* None */
			case 0:
			{
				message(MSG_RECOVER, 0, __T("You are no longer bleeding."));
				if (OPT(disturb_state)) disturb(0, 0);
				break;
			}
		}
		/* Notice */
		notice = TRUE;
	}
	/* Use the value */
	p_ptr->timed[TMD_CUT] = v;

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

	/* Disturb */
	if (OPT(disturb_state)) disturb(0, 0);

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

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

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return (TRUE);
}

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

	bool notice = FALSE;

	/* Hack -- Force good values */
	v = MIN(v, PY_FOOD_UPPER);
	v = MAX(v, 0);

	/* Fainting / Starving */
	if (p_ptr->food < PY_FOOD_FAINT)
	{
		old_aux = 0;
	}
	/* Weak */
	else if (p_ptr->food < PY_FOOD_WEAK)
	{
		old_aux = 1;
	}
	/* Hungry */
	else if (p_ptr->food < PY_FOOD_ALERT)
	{
		old_aux = 2;
	}
	/* Normal */
	else if (p_ptr->food < PY_FOOD_FULL)
	{
		old_aux = 3;
	}
	/* Full */
	else if (p_ptr->food < PY_FOOD_MAX)
	{
		old_aux = 4;
	}
	else /* Gorged */
	{
		old_aux = 5;
	}
	/* Fainting / Starving */
	if (v < PY_FOOD_FAINT)
	{
		new_aux = 0;
	}
	/* Weak */
	else if (v < PY_FOOD_WEAK)
	{
		new_aux = 1;
	}
	/* Hungry */
	else if (v < PY_FOOD_ALERT)
	{
		new_aux = 2;
	}
	/* Normal */
	else if (v < PY_FOOD_FULL)
	{
		new_aux = 3;
	}
	/* Full */
	else if (v < PY_FOOD_MAX)
	{
		new_aux = 4;
	}
	/* Gorged */
	else
	{
		new_aux = 5;
	}
	/* Food increase */
	if (new_aux > old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Weak */
			case 1:
			{
				msg_print(__T("You are still weak."));
				break;
			}
			/* Hungry */
			case 2:
			{
				msg_print(__T("You are still hungry."));
				break;
			}
			/* Normal */
			case 3:
			{
				msg_print(__T("You are no longer hungry."));
				break;
			}
			/* Full */
			case 4:
			{
				msg_print(__T("You are full!"));
				break;
			}
			/* Bloated */
			case 5:
			{
				msg_print(__T("You have gorged yourself!"));
				break;
			}
		}
		/* Change */
		notice = TRUE;
	}
	/* Food decrease */
	else if (new_aux < old_aux)
	{
		/* Describe the state */
		switch (new_aux)
		{
			/* Fainting / Starving */
			case 0:
			{
				sound(MSG_NOTICE);
				msg_print(__T("You are getting faint from hunger!"));
				break;
			}
			/* Weak */
			case 1:
			{
				sound(MSG_NOTICE);
				msg_print(__T("You are getting weak from hunger!"));
				break;
			}
			/* Hungry */
			case 2:
			{
				sound(MSG_HUNGRY);
				msg_print(__T("You are getting hungry."));
				break;
			}
			/* Normal */
			case 3:
			{
				sound(MSG_NOTICE);
				msg_print(__T("You are no longer full."));
				break;
			}
			/* Full */
			case 4:
			{
				sound(MSG_NOTICE);
				msg_print(__T("You are no longer gorged."));
				break;
			}
		}
		/* Change */
		notice = TRUE;
	}

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

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

	/* Disturb */
	if (OPT(disturb_state)) disturb(0, 0);

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

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

	/* Handle stuff */
	handle_stuff();

	/* Result */
	return (TRUE);
}
