/* テスト */
/* 
 * Debug Status: Not checked.
 * - Fixed __T on this page.
 */
/*
 * File: cmd-obj.c
 * Purpose: Handle objects in various ways
 *
 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
 * Copyright (c) 2007 Andrew Sidwell
 *
 * 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"
#include "object/tvalsval.h"
#include "object/object.h"
#include "game-cmd.h"
#include "cmds.h"
#include "effects.h"
}

extern bool get_item(int *cp, const _TCHAR *pmt, const _TCHAR *str, int mode);
extern errr cmd_insert(cmd_code c, ...);
extern const _TCHAR *quark_str(quark_t q);
extern unsigned check_for_inscrip(const object_type *o_ptr, const _TCHAR *inscrip);
extern void message_flush(void);
extern void screen_save(void);
extern void screen_load(void);
extern void msg_print(const _TCHAR *msg);
extern bool get_check(const _TCHAR *prompt);
extern bool (*item_tester_hook)(const object_type*);
extern size_t object_desc(_TCHAR *buf, size_t max, const object_type *o_ptr, bool prefix, int mode);
extern size_t strnfmt(_TCHAR *buf, size_t max, const _TCHAR *fmt, ...);

/*** Utility bits and bobs ***/ 
/* 
 * Check to see if the player can use a rod/wand/staff/activatable object. 
 */ 
static int check_devices(object_type *o_ptr) 
{ 
	int lev, chance; 
	const _TCHAR *msg; 
	const _TCHAR *what = NULL; 

	/* Get the right string */ 
	switch (o_ptr->tval) 
	{ 
		case TV_ROD:   
			msg = __T("zap the rod");   
			break; 
		case TV_WAND:  
			msg = __T("use the wand");  
			what = __T("wand");  
			break; 
		case TV_STAFF: 
			msg = __T("use the staff"); 
			what = __T("staff"); 
			break; 
		default:       
			msg = __T("activate it");  
			break; 
	} 
	/* Extract the item level */ 
	if (artifact_p(o_ptr)) 
		lev = a_info[o_ptr->name1].level; 
	else 
		lev = k_info[o_ptr->k_idx].level; 

	/* Base chance of success */ 
	chance = p_ptr->state.skills[SKILL_DEVICE]; 

	/* Confusion hurts skill */ 
	if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_AMNESIA]) 
		chance = chance / 2; 

	/* High level objects are harder */ 
		chance -= MIN(lev, 50); 

	/* Give everyone a (slight) chance */ 
	if ((chance < USE_DEVICE) && one_in_(USE_DEVICE - chance + 1)) 
	{ 
		chance = USE_DEVICE; 
	} 
	/* Roll for usage */ 
	if ((chance < USE_DEVICE) || (randint1(chance) < USE_DEVICE)) 
	{ 
		if (OPT(flush_failure)) flush(); 
		msg_format(__T("You failed to %s properly."), msg); 
		return FALSE; 
	} 
	/* Notice empty staffs */ 
	if (what && o_ptr->pval <= 0) 
	{ 
		if (OPT(flush_failure)) flush(); 
		msg_format(__T("The %s has no charges left."), what); 
		o_ptr->ident |= (IDENT_EMPTY); 
		return FALSE; 
	} 
	return TRUE; 
} 

/* 
 * Return the chance of an effect beaming, given a tval. 
 */ 
static int beam_chance(int tval) 
{ 
	switch (tval) 
	{ 
		case TV_WAND: return 20; 
		case TV_ROD:  return 10; 
	} 
	return 0; 
} 

typedef enum 
{  
	ART_TAG_NONE,  
	ART_TAG_NAME,  
	ART_TAG_KIND,  
	ART_TAG_VERB,  
	ART_TAG_VERB_IS  
} art_tag_t; 

static art_tag_t art_tag_lookup(const _TCHAR *tag) 
{ 
	if (_tcsnccmp(tag, __T("name"), 4) == 0) 
		return ART_TAG_NAME; 
	else if (_tcsnccmp(tag, __T("kind"), 4) == 0) 
		return ART_TAG_KIND; 
	else if (_tcsnccmp(tag, __T("s"), 1) == 0) 
		return ART_TAG_VERB; 
	else if (_tcsnccmp(tag, __T("is"), 2) == 0) 
		return ART_TAG_VERB_IS; 
	else 
		return ART_TAG_NONE; 
} 

/* 
 * Print an artifact activation message. 
 *  
 * In order to support randarts, with scrambled names, we re-write 
 * the message to replace instances of {name} with the artifact name 
 * and instances of {kind} with the type of object. 
 * 
 * This code deals with plural and singular forms of verbs correctly  
 * when encountering {s}, though in fact both names and kinds are  
 * always singular in the current code (gloves are "Set of" and boots 
 * are "Pair of") 
 */ 
static void activation_message(object_type *o_ptr, const _TCHAR *message) 
{ 
	_TCHAR buf[1024] = __T("\0"); 
	const _TCHAR *next; 
	const _TCHAR *s; 
	const _TCHAR *tag; 
	const _TCHAR *in_cursor; 
	size_t end = 0; 

	in_cursor = message; 

	next = _tcschr(in_cursor, '{'); 
	while (next) 
	{ 
		/* Copy the text leading up to this { */ 
		strnfcat(buf, 1024, &end, __T("%.*s"), next - in_cursor, in_cursor);  

		s = next + 1; 
		while (*s && isalpha((_TCHAR) *s)) s++; 

		if (*s == '}')          /* Valid tag */ 
		{ 
			tag = next + 1; /* Start the tag after the { */ 
			in_cursor = s + 1; 

			switch(art_tag_lookup(tag)) 
			{ 
				case ART_TAG_NAME: 
					end += object_desc(buf, 1024, o_ptr, TRUE, ODESC_BASE);  
					break; 
				case ART_TAG_KIND: 
					object_kind_name(&buf[end], 1024-end, o_ptr->k_idx, TRUE); 
                    end += _tcsclen(&buf[end]); 
                    break; 
                    case ART_TAG_VERB: 
                            strnfcat(buf, 1024, &end, __T("s")); 
                            break; 
                    case ART_TAG_VERB_IS: 
                            if((end > 2) && (buf[end-2] == 's')) 
                                    strnfcat(buf, 1024, &end, __T("are")); 
                            else 
                                    strnfcat(buf, 1024, &end, __T("is")); 
                    default: 
                            break; 
                    } 
                } 
                else    /* An invalid tag, skip it */ 
                { 
                        in_cursor = next + 1; 
                }  
 
                next = _tcschr(in_cursor, '{'); 
        } 
        strnfcat(buf, 1024, &end, in_cursor); 
  
        msg_print(buf); 
}  

/*** Inscriptions ***/

/* Remove inscription */
void do_cmd_uninscribe(cmd_code code, cmd_arg args[]) 
{ 
	object_type *o_ptr = object_from_item_idx(args[0].item); 

	if (obj_has_inscrip(o_ptr)) 
 		msg_print(__T("Inscription removed."));

	o_ptr->note = 0;

	p_ptr->notice |= (PN_COMBINE | PN_SQUELCH);
	p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
}

/* Add inscription */

void do_cmd_inscribe(cmd_code code, cmd_arg args[]) 
{ 
	object_type *o_ptr = object_from_item_idx(args[0].item); 

	o_ptr->note = quark_add(args[1].string); 

	p_ptr->notice |= (PN_COMBINE | PN_SQUELCH); 
	p_ptr->redraw |= (PR_INVEN | PR_EQUIP); 
} 

static void obj_inscribe(object_type *o_ptr, int item)
{
	_TCHAR o_name[80];
	_TCHAR tmp[80] = __T("");
	UNREFERENCED_PARAMETER(item);

	object_desc(o_name, _countof(o_name), o_ptr, TRUE, ODESC_FULL);
	msg_format(__T("Inscribing %s."), o_name);
	message_flush();

	/* Use old inscription */
	if (o_ptr->note)
		strnfmt(tmp, _countof(tmp), __T("%s"), quark_str(o_ptr->note));

	/* Get a new inscription (possibly empty) */
	if (get_string(__T("Inscription: "), tmp, _countof(tmp)))
	{
		cmd_insert(CMD_INSCRIBE, item, tmp);	
	}
}

/*** Examination ***/
static void obj_examine(object_type *o_ptr, int item)
{
	_TCHAR langbuf[200];
	UNREFERENCED_PARAMETER(item);
	text_out_hook = text_out_to_screen;
	screen_save();

	object_info_header(o_ptr);
	if (!object_info(o_ptr, FALSE))
		text_out(__T("\n\nThis item does not seem to possess any special abilities."));

	text_out_c(TERM_L_BLUE, LS(__T("\n\n[Press any key to continue.]\n"),
			spaceme(langbuf,__T("\n\n[何かキーを押してください]\n"))));

	(void)anykey();

	screen_load();
}

/*** Taking off/putting on ***/

/* Take off an item */
void do_cmd_takeoff(cmd_code code, cmd_arg args[]) 
{ 
	int item = args[0].item;

	if (!item_is_available(item, NULL, USE_EQUIP)) 
	{ 
        msg_print(__T("You are not wielding that item.")); 
        return; 
	} 
	if (!obj_can_takeoff(object_from_item_idx(item))) 
	{ 
        msg_print(__T("You cannot take off that item.")); 
        return; 
	}
	inven_takeoff(item, 255);
	p_ptr->energy_use = 50;
}

/* Wield or wear an item */
void do_cmd_wield(cmd_code code, cmd_arg args[]) 
{
	int slot;
	object_type *equip_o_ptr;

	_TCHAR o_name[80];

	unsigned n;

	int item = args[0].item; 
 	
 	object_type *o_ptr = object_from_item_idx(item);

	if (!item_is_available(item, NULL, USE_INVEN | USE_FLOOR)) 
 	{ 
 		msg_print(__T("You do not have that item to wield.")); 
 		return; 
 	}
	/* Check the slot */
	slot = wield_slot(o_ptr);
	equip_o_ptr = &inventory[slot];

	/* Check for existing wielded item */
	if (equip_o_ptr)
	{
		/* Prevent wielding into a cursed slot */
		if (cursed_p(equip_o_ptr))
		{
			/* Message */
			object_desc(o_name, _countof(o_name), equip_o_ptr, FALSE, ODESC_BASE);
			msg_format(__T("The %s you are %s appears to be cursed."),
			           o_name, describe_use(slot));

			return;
		}
		/* "!t" checks for taking off */
		n = check_for_inscrip(equip_o_ptr, __T("!t"));
		while (n--)
		{
			/* Prompt */
			object_desc(o_name, _countof(o_name), equip_o_ptr, TRUE, ODESC_FULL);

			/* Forget it */
			if (!get_check(format(__T("Really take off %s? "), o_name))) return;
		}
	}
	wield_item(o_ptr, item);
}

/* Drop an item */
void do_cmd_drop(cmd_code code, cmd_arg args[]) 
{ 
	int item = args[0].item; 
	object_type *o_ptr = object_from_item_idx(item);
	int amt = args[1].number; 

	if (!item_is_available(item, NULL, USE_INVEN | USE_EQUIP)) 
	{ 
		msg_print(__T("You do not have that item to drop it.")); 
		return; 
	} 
	/* Hack -- Cannot remove cursed items */ 
	if ((item >= INVEN_WIELD) && cursed_p(o_ptr)) 
	{ 
		msg_print(__T("Hmmm, it seems to be cursed.")); 
		return; 
	} 
	inven_drop(item, amt); 
	p_ptr->energy_use = 50; 
}

static void obj_drop(object_type *o_ptr, int item) 
{ 
	int amt;

	amt = get_quantity(NULL, o_ptr->number);
	if (amt <= 0) return;

	cmd_insert(CMD_DROP, item, amt);
}

/*** Casting and browsing ***/

/* Peruse spells in a book */
static void obj_browse(object_type *o_ptr, int item)
{
	UNREFERENCED_PARAMETER(item);
	do_cmd_browse_aux(o_ptr);
}

/* Study a book to gain a new spell */
static void obj_study(object_type *o_ptr, int item)
{
	UNREFERENCED_PARAMETER(item);

	/* Track the object kind */
	object_kind_track(o_ptr->k_idx);
	handle_stuff();

	/* Mage -- Choose a spell to study */ 
	if (cp_ptr->flags & CF_CHOOSE_SPELLS) 
	{ 
		int spell = get_spell(o_ptr, __T("study"), FALSE, FALSE); 
		if (spell >= 0) 
			cmd_insert(CMD_STUDY_SPELL, spell); 
	} 
	/* Priest -- Choose a book to study */ 
	else 
	{ 
		cmd_insert(CMD_STUDY_BOOK, item); 
	}
}

static void obj_cast(object_type *o_ptr, int item)
{
	int spell, dir = DIR_UNKNOWN;
	const _TCHAR *verb = ((cp_ptr->spell_book == TV_MAGIC_BOOK) ? __T("cast") : __T("recite"));
	const _TCHAR *noun = ((cp_ptr->spell_book == TV_MAGIC_BOOK) ? __T("spell") : __T("prayer"));
	UNREFERENCED_PARAMETER(item);
	UNREFERENCED_PARAMETER(o_ptr);

	/* Track the object kind */
	object_kind_track(o_ptr->k_idx);
	handle_stuff();

	/* Ask for a spell */
	spell = get_spell(o_ptr, verb, TRUE, FALSE);
	if (spell < 0)
	{
		if (spell == -2) 
			msg_format(__T("You don't know any %ss in that book."), noun);
		return;
	}
 	if (spell_needs_aim(cp_ptr->spell_book, spell) && !get_aim_dir(&dir)) 
 		return; 

	cmd_insert(CMD_CAST, spell, dir);
}

/*** Using items the traditional way ***/

/* 
 * Use an object the right way. 
 * 
 * There may be a BIG problem with any "effect" that can cause "changes" 
 * to the inventory.  For example, a "scroll of recharging" can cause 
 * a wand/staff to "disappear", moving the 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. 
 */ 
void do_cmd_use(cmd_code code, cmd_arg args[]) 
{ 
	int item = args[0].item; 
    object_type *o_ptr = object_from_item_idx(item); 
    int effect; 
    bool ident = FALSE, used; 
    bool was_aware = object_aware_p(o_ptr); 
    int dir = 5; 
    int px = p_ptr->px, py = p_ptr->py; 
    int snd; 
    use_type use;
	int items_allowed = 0;

    /* Determine how this item is used. */ 
    if (obj_is_rod(o_ptr)) 
    { 
		if (!obj_can_zap(o_ptr)) 
 		{ 
 			msg_print(__T("The rod is not yet recharged.")); 
 			return; 
 		} 
		use = USE_TIMEOUT; 
		snd = MSG_ZAP_ROD; 
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_wand(o_ptr)) 
    { 
		use = USE_CHARGE; 
		snd = MSG_ZAP_ROD;
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_staff(o_ptr)) 
    {        
		use = USE_CHARGE; 
		snd = MSG_ZAP_ROD;
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_food(o_ptr)) 
    { 
		use = USE_SINGLE; 
		snd = MSG_EAT;           
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_potion(o_ptr)) 
    { 
		use = USE_SINGLE; 
		snd = MSG_QUAFF;  
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_scroll(o_ptr)) 
    { 
		/* Check player can use scroll */ 
 		if (!player_can_read()) 
 			return; 
		use = USE_SINGLE; 
		snd = MSG_GENERIC;               
		items_allowed = USE_INVEN | USE_FLOOR;
    } 
    else if (obj_is_activatable(o_ptr))
    { 
		if (!obj_can_activate(o_ptr)) 
		{ 
			msg_print(__T("The item is not ready to activate")); 
			return; 
		}
		use = USE_TIMEOUT; 
		snd = MSG_ACT_ARTIFACT;
 		items_allowed = USE_EQUIP;
	} 
 	else 
 	{ 
 		msg_print(__T("The item cannot be used at the moment"));
	} 
	/* Check if item is within player's reach. */ 
	if (items_allowed == 0 || !item_is_available(item, NULL, items_allowed)) 
	{ 
		msg_print(__T("You cannot use that item from its current location.")); 
		return; 
	} 
    /* Figure out effect to use */ 
    if (o_ptr->name1) 
		effect = a_info[o_ptr->name1].effect; 
    else 
		effect = k_info[o_ptr->k_idx].effect; 

    /* If the item requires a direction, get one (allow cancelling) */       
    if (obj_needs_aim(o_ptr)) 
    { 
		dir = args[1].direction;
    } 
    /* Use energy regardless of failure */ 
    p_ptr->energy_use = 100; 

    /* Check for use */ 
    if (use == USE_CHARGE || use == USE_TIMEOUT) 
    { 
		if (!check_devices(o_ptr)) 
			return; 
    } 
    /* Special message for artifacts */ 
    if (artifact_p(o_ptr)) 
    { 
		message(snd, 0, __T("You activate it.")); 
		activation_message(o_ptr, a_text + a_info[o_ptr->name1].effect_msg); 
    } 
    else 
    { 
		/* Make a noise! */ 
		sound(snd); 
    } 
    /* A bit of a hack to make ID work better. 
       -- Check for "obvious" effects beforehand. */ 
    if (effect_obvious(effect)) 
		object_aware(o_ptr); 

    /* Do effect */ 
    used = effect_do(effect, &ident, was_aware, dir, beam_chance(o_ptr->tval)); 

    /* Food feeds the player */ 
    if (o_ptr->tval == TV_FOOD || o_ptr->tval == TV_POTION) 
		(void)set_food(p_ptr->food + o_ptr->pval); 

    if (!used && !ident) 
		return; 

    /* Mark as tried and redisplay */ 
    p_ptr->notice |= (PN_COMBINE | PN_REORDER); 
    p_ptr->redraw |= (PR_INVEN | PR_EQUIP); 

    /* 
     * If the player becomes aware of the item's function, then mark it as 
     * aware and reward the player with some experience.  Otherwise, mark 
     * it as "tried". 
     */ 
    if (ident && !was_aware) 
    { 
            /* Object level */ 
            int lev = k_info[o_ptr->k_idx].level; 

            object_aware(o_ptr); 
            if (o_ptr->tval == TV_ROD) object_known(o_ptr); 
            gain_exp((lev + (p_ptr->lev / 2)) / p_ptr->lev); 
            p_ptr->notice |= PN_SQUELCH; 
    } 
    else 
    { 
            object_tried(o_ptr); 
    } 
    /* Chargeables act differently to single-used items when not used up */ 
    if (used && use == USE_CHARGE) 
    { 
		/* Use a single charge */ 
		o_ptr->pval--; 

		/* Describe charges */ 
		if (item >= 0) 
			inven_item_charges(item); 
		else 
			floor_item_charges(0 - item); 
    } 
    else if (used && use == USE_TIMEOUT) 
    { 
		/* Artifacts use their own special field */ 
		if (o_ptr->name1) 
		{ 
			const artifact_type *a_ptr = &a_info[o_ptr->name1]; 
			o_ptr->timeout = a_ptr->time_base + damroll(a_ptr->time_dice, a_ptr->time_sides); 
		} 
		else 
		{ 
			const object_kind *k_ptr = &k_info[o_ptr->k_idx]; 
			o_ptr->timeout += k_ptr->time_base + damroll(k_ptr->time_dice, k_ptr->time_sides); 
		} 
    } 
    else if (used && use == USE_SINGLE) 
    { 
		/* 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); 
		} 
	} 
     
    /* Hack to make Glyph of Warding work properly */ 
    if (cave_feat[py][px] == FEAT_GLYPH) 
    { 
		/* Shift any objects to further away */ 
		for (o_ptr = get_first_object(py, px); o_ptr; o_ptr = get_next_object(o_ptr)) 
		{ 
			drop_near(o_ptr, 0, py, px); 
		} 
		/* Delete the "moved" objects from their original position */ 
		delete_object(py, px); 
	} 
} 

/*** Refuelling ***/

void do_cmd_refill(cmd_code code, cmd_arg args[]) 
	{ 
	object_type *j_ptr = &inventory[INVEN_LITE]; 
	u32b f[OBJ_FLAG_N];

	int item = args[0].item; 
	object_type *o_ptr = object_from_item_idx(item);

	if (!item_is_available(item, NULL, USE_INVEN | USE_FLOOR)) 
	{ 
		msg_print(__T("You do not have that item to refill with it.")); 
		return; 
	} 
	/* Check what we're wielding. */ 
	object_flags(j_ptr, f); 

	if (j_ptr->tval != TV_LITE) 
	{ 
		msg_print(__T("You are not wielding a light.")); 
		return; 
	} 
	else if (f[2] & TR2_NO_FUEL) 
	{ 
		msg_print(__T("Your light cannot be refilled.")); 
		return; 
	} 
	/* It's a lamp */
	if (j_ptr->sval == SV_LITE_LANTERN)
		refill_lamp(j_ptr, o_ptr, item);

	/* It's a torch */
	else if (j_ptr->sval == SV_LITE_TORCH)
		refuel_torch(j_ptr, o_ptr, item);

	p_ptr->energy_use = 50;
}

/*** Handling bits ***/

/* Item "action" type */
typedef struct
{
	void (*action)(object_type *, int);
	cmd_code command;
	const _TCHAR *desc;

	const _TCHAR *prompt;
	const _TCHAR *noop;

	bool (*filter)(const object_type *o_ptr);
	int mode;
	bool (*prereq)(void);
} item_act_t;

/* All possible item actions */
static item_act_t item_actions[] =
{
	/* Not setting IS_HARMLESS for this one because it could cause a true
	 * dangerous command to not be prompted, later.
	 */
	{ NULL, CMD_UNINSCRIBE, __T("uninscribe"),
	  __T("Un-inscribe which item? "), __T("You have nothing to un-inscribe."),
	  obj_has_inscrip, (USE_EQUIP | USE_INVEN | USE_FLOOR), NULL },

	{ obj_inscribe, CMD_NULL, __T("inscribe"),
	  __T("Inscribe which item? "), __T("You have nothing to inscribe."),
	  NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR | IS_HARMLESS), NULL },

	{ obj_examine, CMD_NULL, __T("examine"), 
	  __T("Examine which item? "), __T("You have nothing to examine."),
	  NULL, (USE_EQUIP | USE_INVEN | USE_FLOOR | IS_HARMLESS), NULL },

	/*** Takeoff/drop/wear ***/
	{ NULL, CMD_TAKEOFF, __T("takeoff"),
	  __T("Take off which item? "), __T("You are not wearing anything you can take off."),
	  obj_can_takeoff, USE_EQUIP, NULL },

	{ NULL, CMD_WIELD, __T("wield"),
	  __T("Wear/Wield which item? "), __T("You have nothing you can wear or wield."),
	  obj_can_wear, (USE_INVEN | USE_FLOOR), NULL },

	{ obj_drop, CMD_NULL, __T("drop"),
	  __T("Drop which item? "), __T("You have nothing to drop."),
	  NULL, (USE_EQUIP | USE_INVEN), NULL },

	/*** Spellbooks ***/
	{ obj_browse, CMD_NULL, __T("browse"),
	  __T("Browse which book? "), __T("You have no books that you can read."),
	  obj_can_browse, (USE_INVEN | USE_FLOOR | IS_HARMLESS), NULL },

	{ obj_study, CMD_NULL, __T("study"),
	  __T("Study which book? "), __T("You have no books that you can read."),
	  obj_can_browse, (USE_INVEN | USE_FLOOR), player_can_study },

	{ obj_cast, CMD_NULL, __T("cast"),
	  __T("Use which book? "), __T("You have no books that you can read."),
	  obj_can_browse, (USE_INVEN | USE_FLOOR), player_can_cast },

	/*** Item usage ***/
	{ NULL, CMD_USE_STAFF, __T("use"), 
	  __T("Use which staff? "), __T("You have no staff to use."),
	  obj_is_staff, (USE_INVEN | USE_FLOOR), NULL },

	{ NULL, CMD_USE_WAND, __T("aim"),
      __T("Aim which wand? "), __T("You have no wand to aim."),
	  obj_is_wand, (USE_INVEN | USE_FLOOR), NULL },

	{ NULL, CMD_USE_ROD, __T("zap"),
      __T("Zap which rod? "), __T("You have no charged rods to zap."),
	  obj_can_zap, (USE_INVEN | USE_FLOOR), NULL },

	{ NULL, CMD_ACTIVATE, __T("activate"),
      __T("Activate which item? "), __T("You have nothing to activate."),
	  obj_can_activate, USE_EQUIP, NULL },

	{ NULL, CMD_EAT, __T("eat"), 
      __T("Eat which item? "), __T("You have nothing to eat."),
	  obj_is_food, (USE_INVEN | USE_FLOOR), NULL },

	{ NULL, CMD_QUAFF, __T("quaff"),
      __T("Quaff which potion? "), __T("You have no potions to quaff."),
	  obj_is_potion, (USE_INVEN | USE_FLOOR), NULL },

	{ NULL, CMD_READ_SCROLL, __T("read"),
      __T("Read which scroll? "), __T("You have no scrolls to read."),
	  obj_is_scroll, (USE_INVEN | USE_FLOOR), player_can_read },

	{ NULL, CMD_REFILL, __T("refill"), 
      __T("Refuel with what fuel source? "), __T("You have nothing to refuel with."),
	  obj_can_refill, (USE_INVEN | USE_FLOOR), NULL },
};

/* List matching up to item_actions[] */
typedef enum
{
	ACTION_UNINSCRIBE = 0,
	ACTION_INSCRIBE,
	ACTION_EXAMINE,
	ACTION_TAKEOFF,
	ACTION_WIELD,
	ACTION_DROP,

	ACTION_BROWSE,
	ACTION_STUDY,
	ACTION_CAST,

	ACTION_USE_STAFF,
	ACTION_AIM_WAND,
	ACTION_ZAP_ROD,
	ACTION_ACTIVATE,
	ACTION_EAT_FOOD,
	ACTION_QUAFF_POTION,
	ACTION_READ_SCROLL,
	ACTION_REFILL
} item_act;

/*** Old-style noun-verb functions ***/

/* Generic "do item action" function */
static void do_item(item_act act)
{
	int item;
	object_type *o_ptr;

	const _TCHAR *q;
	const _TCHAR *s;

	if (item_actions[act].prereq)
	{
		if (!item_actions[act].prereq())
			return;
	}
	/* Get item */
	q = item_actions[act].prompt;
	s = item_actions[act].noop;
	item_tester_hook = item_actions[act].filter;
	if (!get_item(&item, q, s, item_actions[act].mode)) return;

	/* Get the item */
	o_ptr = object_from_item_idx(item); 

	if (item_actions[act].action != NULL) 
		item_actions[act].action(o_ptr, item); 	
	else if (obj_needs_aim(o_ptr)) 
 	{ 
		int dir; 
		if (!get_aim_dir(&dir)) 
			return; 

		cmd_insert(item_actions[act].command, item, dir); 
	}
	else
		cmd_insert(item_actions[act].command, item);
}

/* Wrappers */
void textui_cmd_uninscribe(void) { do_item(ACTION_UNINSCRIBE); }
void textui_cmd_inscribe(void) { do_item(ACTION_INSCRIBE); }
void do_cmd_observe(void) { do_item(ACTION_EXAMINE); }
void textui_cmd_takeoff(void) { do_item(ACTION_TAKEOFF); }
void textui_cmd_wield(void) { do_item(ACTION_WIELD); }
void textui_cmd_drop(void) { do_item(ACTION_DROP); }
void do_cmd_browse(void) { do_item(ACTION_BROWSE); }
void textui_cmd_study(void) { do_item(ACTION_STUDY); }
void textui_cmd_cast(void) { do_item(ACTION_CAST); }
void textui_cmd_pray(void) { do_item(ACTION_CAST); }
void textui_cmd_use_staff(void) { do_item(ACTION_USE_STAFF); }
void textui_cmd_aim_wand(void) { do_item(ACTION_AIM_WAND); }
void textui_cmd_zap_rod(void) { do_item(ACTION_ZAP_ROD); }
void textui_cmd_activate(void) { do_item(ACTION_ACTIVATE); }
void textui_cmd_eat_food(void) { do_item(ACTION_EAT_FOOD); }
void textui_cmd_quaff_potion(void) { do_item(ACTION_QUAFF_POTION); }
void textui_cmd_read_scroll(void) { do_item(ACTION_READ_SCROLL); }
void textui_cmd_refill(void) { do_item(ACTION_REFILL); }
