/* File: cmd4.c */

/* Purpose: Interface 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"



/*
 * Hack -- redraw the screen
 *
 * This command performs various low level updates, clears all the "extra"
 * windows, does a total redraw of the main window, and requests all of the
 * interesting updates and redraws that I can think of.
 */
void do_cmd_redraw(void)
{
    /* Hack -- react to changes */
    Term_xtra(TERM_XTRA_REACT, 0);


    /* Verify the keymap */
    keymap_init();


#ifdef GRAPHIC_RECALL
    
    /* Hack -- erase the "recall" window */
    if (term_recall) {
        Term_activate(term_recall);
        Term_clear();
        Term_fresh();
    }

#endif
    
#ifdef GRAPHIC_CHOICE

    /* Hack -- erase the "choice" window */
    if (term_choice) {
        Term_activate(term_choice);
        Term_clear();
        Term_fresh();
    }

#endif

#ifdef GRAPHIC_MIRROR

    /* Hack -- erase the "mirror" window */
    if (term_mirror) {
        Term_activate(term_mirror);
        Term_clear();
        Term_fresh();
    }

#endif

    /* Activate the main screen */
    Term_activate(term_screen);

    /* Actual redraw */
    Term_redraw();


    /* Combine and Reorder the pack */
    p_ptr->update |= (PU_COMBINE | PU_REORDER);

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

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

    /* Forget lite/view */
    p_ptr->update |= (PU_UN_VIEW | PU_UN_LITE);

    /* Update lite/view */
    p_ptr->update |= (PU_VIEW | PU_LITE);
    
    /* Update monsters */
    p_ptr->update |= (PU_MONSTERS);

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

    /* Redraw the recall window */
    p_ptr->redraw |= (PR_RECENT);
    
    /* Redraw the choice window */
    p_ptr->redraw |= (PR_CHOOSE);

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

    /* Hack -- refresh */
    Term_fresh();
}


/*
 * Hack -- change name
 */
void do_cmd_change_name(void)
{
    char	c;

    bool	flag;

    bool	history = FALSE;
    
    char	tmp[160];


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();

    /* Get command */
    for (flag = FALSE; !flag; ) {

        /* Display the player */
        display_player(history);

        /* Prompt */
        prt("['C' to change name, 'F' to dump file, 'H' for history, or ESCAPE]", 21, 2);

        /* Query */
        c = inkey();

        /* Handle */
        switch (c) {

          case 'C':
          case 'c':
            get_name();
            flag = TRUE;
            break;

          case 'F':
          case 'f':
            prt("File name: ", 0, 0);
            sprintf(tmp, "%s.txt", player_base);
            if (askfor_aux(tmp, 60) && tmp[0]) {
                file_character(tmp, FALSE);
            }
            break;

          case 'H':
          case 'h':
            history = !history;
            break;

          case ESCAPE:
          case ' ':
          case '\n':
          case '\r':
            flag = TRUE;
            break;

          default:
            bell();
            break;
        }

        /* Flush messages */
        msg_print(NULL);
    }

    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}


/*
 * Recall the most recent message
 */
void do_cmd_message_one(void)
{
    /* Recall */
    prt(format("> %s", message_str(0)), 0, 0);
}


/*
 * Show previous messages to the user	-BEN-
 *
 * The screen format uses line 0 and 23 for headers and prompts,
 * skips line 1 and 22, and uses line 2 thru 21 for old messages.
 *
 * Hack -- attempt to combine identical messages ala "[xNN]" suffix,
 * as in "You hit the monster. [x4]", if the user has requested this.
 */
void do_cmd_messages(void)
{
    int i = 0, j = 0, k = 0, n = 0, t = 0;

    bool find = FALSE;
    
    char finder[80] = "";

    cptr str[MESSAGE_MAX];
    
    u16b cnt[MESSAGE_MAX];
    

    /* Total messages */
    t = message_num();

    /* Prepare the arrays */
    for (j = 0; j < t; j++) {

        /* Get the message */
        cptr msg = message_str(j);
        
        /* Process identical messages (if requested) */
        if (n && streq(str[n-1], msg) && optimize_various) {

            /* Count identical messages */
            cnt[n-1]++;
        }
        
        /* Process new messages */
        else {

            /* Remember the message */
            str[n] = msg;

            /* Count the message */
            cnt[n] = 0;
            
            /* Advance */
            n++;
        }
    }


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();

    /* Process requests until done */
    while (1) {

        /* Clear the screen */
        clear_screen();

        /* Dump up to 20 lines of messages */
        for (j = 0; (j < 20) && (i + j < n); j++) {

            byte a = TERM_WHITE;

            /* Handle searches */
            if (find && strstr(str[i+j], finder)) a = TERM_YELLOW;

            /* Begin the next message */
            Term_gotoxy(0, 21-j);
            
            /* Dump the messages, bottom to top */
            Term_addstr(-1, a, str[i+j]);

            /* Hack -- add the message counter */
            if (cnt[i+j]) {

                /* Hack -- add the message counter */
                Term_addstr(-1, a, format(" [x%d]", cnt[i+j] + 1));
            }
        }

        /* Display header */
        prt(format("Message Recall (%d total, %d unique)", t, n), 0, 0);

        /* Display prompt */
        prt("[Press 'p' for older, 'n' for newer, '/' for search, or ESCAPE]", 23, 0);

        /* Get a command */
        k = inkey();

        /* Exit on Escape */
        if (k == ESCAPE) break;

        /* Hack -- Save the old index */
        j = i;

        /* Hack -- handle search */
        if (k == '/') {

            /* Cancel find */
            find = FALSE;

            /* Prompt */
            prt("Find: ", 23, 0);
                    
            /* Get a search stringm activate search */
            if (askfor_aux(finder, 80)) find = TRUE;
            
            /* Okay */
            continue;
        }

        /* Recall more older messages */
        if ((k == 'p') || (k == KTRL('P'))) {

            /* Go older if legal */
            if (i + 20 < n) i += 20;
        }

        /* Recall more newer messages */
        if ((k == 'n') || (k == KTRL('N'))) {

            /* Go newer if legal */
            if (i >= 20) i -= 20;
        }

        /* Hack -- Error of some kind */
        if (i == j) bell();
    }

    /* Restore the screen */
    Term_load();
    
    /* Leave "icky" mode */
    character_icky = FALSE;
}


/*
 * Interact with some options
 */
static void do_cmd_options_aux(int page, cptr info)
{
    char	ch;

    int		i, n = 0;

    int		opt[24];

    char	buf[80];


    /* Lookup the options */
    for (i = 0; i < 24; i++) opt[i] = 0;

    /* Scan the options */
    for (i = 0; options[i].o_desc; i++) {

        /* Notice options on this "page" */
        if (options[i].o_page == page) opt[n++] = i;
    }


    /* Clear the screen */
    clear_screen();

    /* Prompt */
    sprintf(buf, "%s (RET to advance, y/n to set, ESC to accept) ", info);
    prt(buf, 0, 0);

    /* Display the options */
    for (i = 0; i < n; i++) {
        sprintf(buf, "%-48s: %s ", options[opt[i]].o_desc,
                (*options[opt[i]].o_var ? "yes" : "no "));
        prt(buf, i + 2, 0);
    }


    /* Start at the first option */
    i = 0;

    /* Interact with the player */
    while (TRUE) {

        move_cursor(i + 2, 50);

        ch = inkey();

        switch (ch) {

          case ESCAPE:	
            return;

          case '-':
          case '8':
            i = (n + i - 1) % n;
            break;

          case ' ':
          case '\n':
          case '\r':
          case '2':
            i = (i + 1) % n;
            break;

          case 'y':
          case 'Y':
          case '6':
            put_str("yes ", i + 2, 50);
            (*options[opt[i]].o_var) = TRUE;
            i = (i + 1) % n;
            break;

          case 'n':
          case 'N':
          case '4':
            put_str("no  ", i + 2, 50);
            (*options[opt[i]].o_var) = FALSE;
            i = (i + 1) % n;
            break;

          default:
            bell();
            break;
        }
    }
}


/*
 * Set or unset various options.
 *
 * Hack -- user must sometimes hit "Ctrl-R" when done
 */
void do_cmd_options(void)
{
    int k;


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();


    /* Interact */
    while (1) {

        /* Clear screen */
        clear_screen();

        /* Why are we here */
        prt("Angband options", 2, 0);

        /* Give some choices */
        prt("(1) User Interface Options", 4, 5);
        prt("(2) Disturbance Options", 5, 5);
        prt("(3) Inventory Options", 6, 5);
        prt("(4) Game-Play Options", 7, 5);
        prt("(5) Efficiency Options", 8, 5);
        prt("(6) Special Options", 9, 5);

        /* Cheating */
        if (can_be_wizard) prt("(C) Cheating Options", 10, 5);

        /* Special choices */
        prt("(D) Base Delay Speed", 11, 5);
        prt("(H) Hitpoint Warning", 12, 5);

        /* Prompt */
        prt("Command: ", 14, 0);
        
        /* Get command */
        k = inkey();

        /* Exit */
        if (k == ESCAPE) break;

        /* General Options */
        if (k == '1') {

            /* Process the general options */
            do_cmd_options_aux(1, "User Interface Options");
        }

        /* Disturbance Options */
        else if (k == '2') {

            /* Process the running options */
            do_cmd_options_aux(2, "Disturbance Options");
        }

        /* Inventory Options */
        else if (k == '3') {

            /* Process the running options */
            do_cmd_options_aux(3, "Inventory Options");
        }

        /* Gameplay Options */
        else if (k == '4') {

            /* Process the game-play options */
            do_cmd_options_aux(4, "Game-Play Options");
        }

        /* Efficiency Options */
        else if (k == '5') {

            /* Process the efficiency options */
            do_cmd_options_aux(5, "Efficiency Options");
        }

        /* Efficiency Options */
        else if (k == '6') {

            /* Process the efficiency options */
            do_cmd_options_aux(6, "Special Options");
        }

        /* Cheating Options XXX XXX XXX */
        else if ((k == 'C') && can_be_wizard) {

            /* Process the cheating options */
            do_cmd_options_aux(255, "Cheating Options");

            /* Hack -- note use of "cheat" options */
            if (cheat_peek) noscore |= 0x0100;
            if (cheat_hear) noscore |= 0x0200;
            if (cheat_room) noscore |= 0x0400;
            if (cheat_xtra) noscore |= 0x0800;
            if (cheat_know) noscore |= 0x1000;
            if (cheat_live) noscore |= 0x2000;
        }

        /* Hack -- Delay Speed */
        else if (k == 'D') {

            /* Prompt */
            prt("Command: Base Delay Speed", 14, 0);
        
            /* Get a new value */
            while (1) {
                prt(format("Current delay speed: %d milliseconds",
                           delay_spd), 18, 0);
                prt("Delay Speed (0-9 or ESC to accept): ", 16, 0);
                k = inkey();
                if (k == ESCAPE) break;
                if (isdigit(k)) delay_spd = D2I(k);
                else bell();
            }
        }

        /* Hack -- hitpoint warning factor */
        else if (k == 'H') {

            /* Prompt */
            prt("Command: Hitpoint Warning", 14, 0);
        
            /* Get a new value */
            while (1) {
                prt(format("Current hitpoint warning: %d0%%",
                           hitpoint_warn), 18, 0);
                prt("Hitpoint Warning (0-9 or ESC to accept): ", 16, 0);
                k = inkey();
                if (k == ESCAPE) break;
                if (isdigit(k)) hitpoint_warn = D2I(k);
                else bell();
            }
        }

        /* Unknown option */
        else {

            /* Oops */
            bell();
        }

        /* Flush messages */
        msg_print(NULL);
    }


    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}



/*
 * Ask for a "user pref line" and process it
 */
void do_cmd_pref(void)
{
    char buf[80];

    /* Wipe it */
    strcpy(buf, "");

    /* Prompt */
    prt("Pref: ", 0, 0);

    /* Ask */
    if (askfor_aux(buf, 80)) {

        /* Process XXX XXX XXX */
        (void)process_pref_file_aux(buf);
    }

    /* Clear */
    prt("", 0, 0);
}


#ifdef ALLOW_MACROS

/*
 * Hack -- append all current macros to the given file
 */
static errr macro_dump(cptr fname)
{
    int i;

    FILE *fff;

    char buf[1024];


    /* Build the filename */
    strcpy(buf, ANGBAND_DIR_USER);
    strcat(buf, fname);
    
#if defined(MACINTOSH) && !defined(applec)
    /* Global -- "text file" */
    _ftype = 'TEXT';
#endif

    /* Append to the file */
    fff = my_fopen(buf, "a");

    /* Failure */
    if (!fff) return (-1);


    /* Skip space */
    fprintf(fff, "\n\n");
    
    /* Start dumping */
    fprintf(fff, "# Automatic macro dump\n\n");

    /* Dump them */
    for (i = 0; i < macro__num; i++) {

        /* Start the macro */
        fprintf(fff, "# Macro '%d'\n\n", i);

        /* Extract the action */
        ascii_to_text(buf, macro__act[i]);

        /* Dump the macro */
        fprintf(fff, "A:%s\n", buf);

        /* Extract the action */
        ascii_to_text(buf, macro__pat[i]);

        /* Dump command macros */
        if (macro__cmd[i]) fprintf(fff, "C:%s\n", buf);

        /* Dump normal macros */
        else fprintf(fff, "P:%s\n", buf);

        /* End the macro */
        fprintf(fff, "\n\n");		
    }

    /* Start dumping */
    fprintf(fff, "\n\n\n\n");


    /* Close */
    my_fclose(fff);

    /* Success */
    return (0);
}


/*
 * Hack -- ask for a "trigger" (see below)
 *
 * Note the complex use of the "inkey()" function from "io.c".
 *
 * The "trigger" must be entered with less than 10 milliseconds
 * between keypresses, as in "inkey_aux()".
 */
static void do_cmd_macro_aux(char *buf)
{
    int i, n = 0, w = 0;

    bool skipping = FALSE;

    char tmp[1024];
    

    /* Important -- Flush input */
    flush();

    /* Do not process macros */
    inkey_base = TRUE;

    /* Attempt to read a key */
    i = inkey();

    /* Save the key */
    buf[n++] = i;

    /* Read the pattern */
    while (TRUE) {

        /* Do not process macros */
        inkey_base = TRUE;

        /* Do not wait for keys */
        inkey_scan = TRUE;

        /* Attempt to read a key */
        i = inkey();

        /* If a key is ready, acquire it */
        if (i) {

            /* Reset delay */
            w = 0;

            /* Hack -- process "ascii 28" */
            if (i == 28) {

                /* Toggle the "skipping" flag */
                skipping = !skipping;
            }

            /* Save usable keys */
            else if (!skipping) {

                /* Save the key */
                buf[n++] = i;
            }

            /* Next */
            continue;
        }

        /* Excessive delay */
        if (w > 30) break;

        /* Increment and delay */
        delay(++w);
    }

    /* Terminate */
    buf[n] = '\0';

    /* Important -- Flush the input */
    flush();
    
    /* Convert the trigger */
    ascii_to_text(tmp, buf);
    
    /* Hack -- display the trigger */
    Term_addstr(-1, TERM_WHITE, tmp);
}

#endif


/*
 * Interact with "macros"
 *
 * Note that the macro "action" must be defined before the trigger.
 *
 * XXX XXX XXX Need messages for success, plus "helpful" info
 */
void do_cmd_macros(void)
{
    int i;

    char tmp[160];

    char buf[1024];


#if defined(MACINTOSH) && !defined(applec)
    _ftype = 'TEXT';
#endif


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();

    /* Process requests until done */
    while (1) {

        /* Clear the screen */
        clear_screen();

        /* Describe */
        Term_putstr(0, 2, -1, TERM_WHITE, "Interact with Macros");


        /* Describe that action */
        Term_putstr(0, 18, -1, TERM_WHITE, "Current action (if any) shown below:");

        /* Analyze the current action */
        ascii_to_text(buf, macro__buf);

        /* Display the current action */
        Term_putstr(0, 20, -1, TERM_WHITE, buf);


        /* Selections */
        Term_putstr(5,  4, -1, TERM_WHITE, "(1) Load a user pref file");
#ifdef ALLOW_MACROS
        Term_putstr(5,  5, -1, TERM_WHITE, "(2) Dump macros");
        Term_putstr(5,  6, -1, TERM_WHITE, "(3) Enter a new action");
        Term_putstr(5,  7, -1, TERM_WHITE, "(4) Query a macro action");
        Term_putstr(5,  8, -1, TERM_WHITE, "(5) Create a command macro");
        Term_putstr(5,  9, -1, TERM_WHITE, "(6) Create a normal macro");
        Term_putstr(5, 10, -1, TERM_WHITE, "(7) Create an identity macro");
        Term_putstr(5, 11, -1, TERM_WHITE, "(8) Create an empty macro");
        Term_putstr(5, 12, -1, TERM_WHITE, "(9) Define a special keymap");
#endif /* ALLOW_MACROS */

        /* Prompt */
        Term_putstr(0, 14, -1, TERM_WHITE, "Command: ");
        
        /* Get a command */
        i = inkey();

        /* Leave */
        if (i == ESCAPE) break;

        /* Load a 'macro' file */
        else if (i == '1') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Load a user pref file");

            /* Get a filename, handle ESCAPE */
            Term_putstr(0, 16, -1, TERM_WHITE, "File: ");

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Ask for a file */
            if (!askfor_aux(tmp, 70)) continue;

            /* Process the given filename */
            (void)process_pref_file(tmp);
        }

#ifdef ALLOW_MACROS

        /* Save a 'macro' file */
        else if (i == '2') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Save a macro file");

            /* Get a filename, handle ESCAPE */
            Term_putstr(0, 16, -1, TERM_WHITE, "File: ");

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Ask for a file */
            if (!askfor_aux(tmp, 70)) continue;

            /* Drop priv's */
            safe_setuid_drop();

            /* Dump the macros */
            (void)macro_dump(tmp);

            /* Grab priv's */
            safe_setuid_grab();
        }
        
        /* Enter a new action */
        else if (i == '3') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Enter a new action");
            
            /* Go to the correct location */
            Term_gotoxy(0, 20);

            /* Hack -- limit the value */
            tmp[80] = '\0';

            /* Get an encoded action */
            if (askfor_aux(buf, 80)) text_to_ascii(macro__buf, buf);
        }

        /* Query a macro action */
        else if (i == '4') {

#if 0
            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Query a macro action");

            /* Prompt */
            Term_putstr(0, 16, -1, TERM_WHITE, "Enter a macro trigger: ");

            /* Get a macro trigger */
            do_cmd_macro_aux(buf);
#endif

            /* XXX XXX XXX */
            msg_print("Command not ready.");
        }
        
        /* Create a command macro */
        else if (i == '5') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Create a command macro");

            /* Prompt */
            Term_putstr(0, 16, -1, TERM_WHITE, "Trigger: ");

            /* Get a macro trigger */
            do_cmd_macro_aux(buf);
            
            /* Link the macro */
            macro_add(buf, macro__buf, TRUE);
            
            /* Message */
            msg_print("Created a new command macro.");
        }
        
        /* Create a normal macro */
        else if (i == '6') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Create a normal macro");

            /* Prompt */
            Term_putstr(0, 16, -1, TERM_WHITE, "Trigger: ");

            /* Get a macro trigger */
            do_cmd_macro_aux(buf);
            
            /* Link the macro */
            macro_add(buf, macro__buf, FALSE);
            
            /* Message */
            msg_print("Created a new normal macro.");
        }
        
        /* Create an identity macro */
        else if (i == '7') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Create an identity macro");

            /* Prompt */
            Term_putstr(0, 16, -1, TERM_WHITE, "Trigger: ");

            /* Get a macro trigger */
            do_cmd_macro_aux(buf);
            
            /* Link the macro */
            macro_add(buf, buf, FALSE);
            
            /* Message */
            msg_print("Created a new identity macro.");
        }
        
        /* Create an empty macro */
        else if (i == '8') {

            /* Prompt */
            Term_putstr(0, 14, -1, TERM_WHITE, "Command: Create an empty macro");

            /* Prompt */
            Term_putstr(0, 16, -1, TERM_WHITE, "Trigger: ");

            /* Get a macro trigger */
            do_cmd_macro_aux(buf);
            
            /* Link the macro */
            macro_add(buf, "", FALSE);
            
            /* Message */
            msg_print("Created a new empty macro.");
        }

        /* Define a keymap */
        else if (i == '9') {

            char i1, i2, i3;

            /* Flush the input */
            flush();

            /* Get the trigger */
            if (get_com("Type the trigger keypress: ", &i1) &&
                get_com("Type the resulting keypress: ", &i2) &&
                get_com("Type a direction (or space): ", &i3)) {

                /* Acquire the array index */
                int k = (byte)(i1);
                int r = (byte)(i2);
                int d = (byte)(i3);

                /* Analyze the result key */
                keymap_cmds[k] = r;

                /* Hack -- Analyze the "direction" (always allow numbers) */
                keymap_dirs[k] = (isdigit(d) ? (d - '0') : keymap_dirs[d]);

                /* Success */
                msg_format("Keypress 0x%02x mapped to 0x%02x/%d",
                           k, keymap_cmds[k], keymap_dirs[k]);
            }
        }

#endif /* ALLOW_MACROS */

        /* Oops */
        else {

            /* Oops */        
            bell();
        }
        
        /* Flush messages */
        msg_print(NULL);
    }
    

    /* Load the screen */
    Term_load();
    
    /* Leave "icky" mode */
    character_icky = FALSE;
}



/*
 * Interact with "visuals"
 */
void do_cmd_visuals(void)
{
    int i;

    FILE *fff;
    
    char tmp[160];

    char buf[1024];


#if defined(MACINTOSH) && !defined(applec)
    _ftype = 'TEXT';
#endif


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();


    /* Interact until done */
    while (1) {

        /* Clear the screen */
        clear_screen();

        /* Ask for a choice */
        prt("Interact with Visuals", 2, 0);

        /* Give some choices */
        prt("(1) Load a user pref file", 4, 5);
#ifndef ANGBAND_LITE
        prt("(2) Dump monster attr/chars", 5, 5);
        prt("(3) Dump object attr/chars", 6, 5);
        prt("(4) Dump feature attr/chars", 7, 5);
        prt("(5) (unused)", 8, 5);
        prt("(6) Change monster attr/chars", 9, 5);
        prt("(7) Change object attr/chars", 10, 5);
        prt("(8) Change feature attr/chars", 11, 5);
        prt("(9) (unused)", 12, 5);
#endif /* ANGBAND_LITE */
        prt("(0) Reset visuals", 13, 5);

        /* Prompt */
        prt("Command: ", 15, 0);

        /* Prompt */
        i = inkey();

        /* Done */
        if (i == ESCAPE) break;

        /* Load a 'pref' file */
        else if (i == '1') {

            /* Prompt */
            prt("Command: Load a user pref file", 15, 0);

            /* Prompt */
            prt("File: ", 17, 0);
            
            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Query */
            if (!askfor_aux(tmp, 70)) continue;

            /* Process the given filename */
            (void)process_pref_file(tmp);
        }

#ifndef ANGBAND_LITE

        /* Dump monster attr/chars */
        else if (i == '2') {

            /* Prompt */
            prt("Command: Dump monster attr/chars", 15, 0);

            /* Prompt */
            prt("File: ", 17, 0);

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Get a filename */
            if (!askfor_aux(tmp, 70)) continue;

            /* Build the filename */
            strcpy(buf, ANGBAND_DIR_USER);
            strcat(buf, tmp);
    
            /* Drop priv's */
            safe_setuid_drop();

            /* Append to the file */
            fff = my_fopen(buf, "a");

            /* Grab priv's */
            safe_setuid_grab();

            /* Failure */
            if (!fff) continue;

            /* Start dumping */
            fprintf(fff, "\n\n");
            fprintf(fff, "# Monster attr/char definitions\n\n");

            /* Dump monsters */
            for (i = 0; i < MAX_R_IDX; i++) {

                monster_race *r_ptr = &r_info[i];

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

                /* Dump a comment */
                fprintf(fff, "# %s\n", (r_name + r_ptr->name));
                
                /* Dump the monster attr/char info */
                fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i,
                        (byte)(r_ptr->l_attr), (byte)(r_ptr->l_char));
            }

            /* All done */
            fprintf(fff, "\n\n\n\n");

            /* Close */
            my_fclose(fff);

            /* Message */
            msg_print("Dumped monster attr/chars.");
        }

        /* Dump object attr/chars */
        else if (i == '3') {

            /* Prompt */
            prt("Command: Dump object attr/chars", 15, 0);

            /* Prompt */
            prt("File: ", 17, 0);

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Get a filename */
            if (!askfor_aux(tmp, 70)) continue;

            /* Build the filename */
            strcpy(buf, ANGBAND_DIR_USER);
            strcat(buf, tmp);
    
            /* Drop priv's */
            safe_setuid_drop();

            /* Append to the file */
            fff = my_fopen(buf, "a");

            /* Grab priv's */
            safe_setuid_grab();

            /* Failure */
            if (!fff) continue;

            /* Start dumping */
            fprintf(fff, "\n\n");
            fprintf(fff, "# Object attr/char definitions\n\n");

            /* Dump objects */
            for (i = 0; i < MAX_K_IDX; i++) {

                inven_kind *k_ptr = &k_info[i];

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

                /* Dump a comment */
                fprintf(fff, "# %s\n", (k_name + k_ptr->name));
                
                /* Dump the object attr/char info */
                fprintf(fff, "K:%d:0x%02X:0x%02X\n\n", i,
                        (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char));
            }

            /* All done */
            fprintf(fff, "\n\n\n\n");

            /* Close */
            my_fclose(fff);

            /* Message */
            msg_print("Dumped object attr/chars.");
        }
        
        /* Dump feature attr/chars */
        else if (i == '4') {

            /* Prompt */
            prt("Command: Dump feature attr/chars", 15, 0);

            /* Prompt */
            prt("File: ", 17, 0);

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Get a filename */
            if (!askfor_aux(tmp, 70)) continue;

            /* Build the filename */
            strcpy(buf, ANGBAND_DIR_USER);
            strcat(buf, tmp);
    
            /* Drop priv's */
            safe_setuid_drop();

            /* Append to the file */
            fff = my_fopen(buf, "a");

            /* Grab priv's */
            safe_setuid_grab();

            /* Failure */
            if (!fff) continue;

            /* Start dumping */
            fprintf(fff, "\n\n");
            fprintf(fff, "# Feature attr/char definitions\n\n");

            /* Dump features */
            for (i = 0; i < MAX_F_IDX; i++) {

                feature_type *f_ptr = &f_info[i];

                /* Skip non-entries */
                if (!f_ptr->name) continue;

                /* Dump a comment */
                fprintf(fff, "# %s\n", (f_name + f_ptr->name));
                
                /* Dump the feature attr/char info */
                fprintf(fff, "F:%d:0x%02X:0x%02X\n\n", i,
                        (byte)(f_ptr->z_attr), (byte)(f_ptr->z_char));
            }

            /* All done */
            fprintf(fff, "\n\n\n\n");

            /* Close */
            my_fclose(fff);

            /* Message */
            msg_print("Dumped feature attr/chars.");
        }

        /* Modify monster attr/chars */
        else if (i == '6') {

            static int r = 0;

            /* Prompt */
            prt("Command: Change monster attr/chars", 15, 0);

            /* Hack -- query until done */
            while (1) {

                monster_race *r_ptr = &r_info[r];

                int da = (byte)(r_ptr->r_attr);
                int dc = (byte)(r_ptr->r_char);
                int ca = (byte)(r_ptr->l_attr);
                int cc = (byte)(r_ptr->l_char);

                /* Label the object */
                Term_putstr(5, 17, -1, TERM_WHITE,
                            format("Monster = %d, Name = %-40.40s",
                                   r, (r_name + r_ptr->name)));

                /* Label the Default values */
                Term_putstr(10, 19, -1, TERM_WHITE,
                            format("Default attr/char = %3u / %3u", da, dc));
                Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 19, da, dc);

                /* Label the Current values */
                Term_putstr(10, 20, -1, TERM_WHITE,
                            format("Current attr/char = %3u / %3u", ca, cc));
                Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 20, ca, cc);

                /* Prompt */
                Term_putstr(0, 22, -1, TERM_WHITE,
                            "Command (n/N/a/A/c/C): ");

                /* Get a command */
                i = inkey();

                /* All done */
                if (i == ESCAPE) break;

                /* Analyze */
                if (i == 'n') r = (r + MAX_R_IDX + 1) % MAX_R_IDX;
                if (i == 'N') r = (r + MAX_R_IDX - 1) % MAX_R_IDX;
                if (i == 'a') r_ptr->l_attr = (byte)(ca + 1);
                if (i == 'A') r_ptr->l_attr = (byte)(ca - 1);
                if (i == 'c') r_ptr->l_char = (byte)(cc + 1);
                if (i == 'C') r_ptr->l_char = (byte)(cc - 1);
            }
        }

        /* Modify object attr/chars */
        else if (i == '7') {

            static int k = 0;

            /* Prompt */
            prt("Command: Change object attr/chars", 15, 0);

            /* Hack -- query until done */
            while (1) {

                inven_kind *k_ptr = &k_info[k];

                int da = (byte)k_ptr->k_attr;
                int dc = (byte)k_ptr->k_char;
                int ca = (byte)k_ptr->x_attr;
                int cc = (byte)k_ptr->x_char;

                /* Label the object */
                Term_putstr(5, 17, -1, TERM_WHITE,
                            format("Object = %d, Name = %-40.40s",
                                   k, (k_name + k_ptr->name)));

                /* Label the Default values */
                Term_putstr(10, 19, -1, TERM_WHITE,
                            format("Default attr/char = %3d / %3d", da, dc));
                Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 19, da, dc);

                /* Label the Current values */
                Term_putstr(10, 20, -1, TERM_WHITE,
                            format("Current attr/char = %3d / %3d", ca, cc));
                Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 20, ca, cc);

                /* Prompt */
                Term_putstr(0, 22, -1, TERM_WHITE,
                            "Command (n/N/a/A/c/C): ");

                /* Get a command */
                i = inkey();

                /* All done */
                if (i == ESCAPE) break;

                /* Analyze */
                if (i == 'n') k = (k + MAX_K_IDX + 1) % MAX_K_IDX;
                if (i == 'N') k = (k + MAX_K_IDX - 1) % MAX_K_IDX;
                if (i == 'a') k_info[k].x_attr = (byte)(ca + 1);
                if (i == 'A') k_info[k].x_attr = (byte)(ca - 1);
                if (i == 'c') k_info[k].x_char = (byte)(cc + 1);
                if (i == 'C') k_info[k].x_char = (byte)(cc - 1);
            }
        }

        /* Modify feature attr/chars */
        else if (i == '8') {

            static int f = 0;

            /* Prompt */
            prt("Command: Change feature attr/chars", 15, 0);

            /* Hack -- query until done */
            while (1) {

                feature_type *f_ptr = &f_info[f];

                int da = (byte)f_ptr->f_attr;
                int dc = (byte)f_ptr->f_char;
                int ca = (byte)f_ptr->z_attr;
                int cc = (byte)f_ptr->z_char;

                /* Label the object */
                Term_putstr(5, 17, -1, TERM_WHITE,
                            format("Terrain = %d, Name = %-40.40s",
                                   f, (f_name + f_ptr->name)));

                /* Label the Default values */
                Term_putstr(10, 19, -1, TERM_WHITE,
                            format("Default attr/char = %3d / %3d", da, dc));
                Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 19, da, dc);

                /* Label the Current values */
                Term_putstr(10, 20, -1, TERM_WHITE,
                            format("Current attr/char = %3d / %3d", ca, cc));
                Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>");
                Term_putch(43, 20, ca, cc);

                /* Prompt */
                Term_putstr(0, 22, -1, TERM_WHITE,
                            "Command (n/N/a/A/c/C): ");

                /* Get a command */
                i = inkey();

                /* All done */
                if (i == ESCAPE) break;

                /* Analyze */
                if (i == 'n') f = (f + MAX_F_IDX + 1) % MAX_F_IDX;
                if (i == 'N') f = (f + MAX_F_IDX - 1) % MAX_F_IDX;
                if (i == 'a') f_info[f].z_attr = (byte)(ca + 1);
                if (i == 'A') f_info[f].z_attr = (byte)(ca - 1);
                if (i == 'c') f_info[f].z_char = (byte)(cc + 1);
                if (i == 'C') f_info[f].z_char = (byte)(cc - 1);
            }
        }

#endif /* ANGBAND_LITE */

        /* Reset visuals */
        else if (i == '0') {

            /* Reset */
            reset_visuals();

            /* Message */
            msg_print("Visual attr/char tables reset.");
        }

        /* Unknown option */
        else {
            bell();
        }
        
        /* Flush messages */
        msg_print(NULL);
    }


    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}


/*
 * Interact with "colors"
 */
void do_cmd_colors(void)
{
    int i;

    FILE *fff;
    
    char tmp[160];
    
    char buf[1024];


#if defined(MACINTOSH) && !defined(applec)
    _ftype = 'TEXT';
#endif


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();


    /* Interact until done */
    while (1) {

        /* Clear the screen */
        clear_screen();

        /* Ask for a choice */
        prt("Interact with Colors", 2, 0);

        /* Give some choices */
        prt("(1) Load a user pref file", 4, 5);
#ifndef ANGBAND_LITE
        prt("(2) Dump colors", 5, 5);
        prt("(3) Modify colors", 6, 5);
#endif /* ANGBAND_LITE */
        
        /* Prompt */
        prt("Command: ", 8, 0);

        /* Prompt */
        i = inkey();

        /* Done */
        if (i == ESCAPE) break;

        /* Load a 'pref' file */
        if (i == '1') {

            /* Prompt */
            prt("Command: Load a user pref file", 8, 0);

            /* Prompt */
            prt("File: ", 10, 0);
            
            /* Default file */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Query */
            if (!askfor_aux(tmp, 70)) continue;

            /* Process the given filename */
            (void)process_pref_file(tmp);

            /* Mega-Hack -- react to changes */
            Term_xtra(TERM_XTRA_REACT, 0);

            /* Mega-Hack -- redraw */
            Term_redraw();
        }

#ifndef ANGBAND_LITE

        /* Dump colors */
        else if (i == '2') {

            /* Prompt */
            prt("Command: Dump colors", 8, 0);

            /* Prompt */
            prt("File: ", 10, 0);

            /* Default filename */
            sprintf(tmp, "user-%s.prf", ANGBAND_SYS);

            /* Get a filename */
            if (!askfor_aux(tmp, 70)) continue;

            /* Build the filename */
            strcpy(buf, ANGBAND_DIR_USER);
            strcat(buf, tmp);
    
            /* Drop priv's */
            safe_setuid_drop();

            /* Append to the file */
            fff = my_fopen(buf, "a");

            /* Grab priv's */
            safe_setuid_grab();

            /* Failure */
            if (!fff) continue;

            /* Start dumping */
            fprintf(fff, "\n\n");
            fprintf(fff, "# Color redefinitions\n\n");

            /* Dump colors */
            for (i = 0; i < 256; i++) {

                int kv = color_table[i][0];
                int rv = color_table[i][1];
                int gv = color_table[i][2];
                int bv = color_table[i][3];
                
                cptr name = "unknown";

                /* Skip non-entries */
                if (!kv && !rv && !gv && !bv) continue;

                /* Extract the color name */
                if (i < 16) name = color_names[i];
                
                /* Dump a comment */
                fprintf(fff, "# Color '%s'\n", name);
                
                /* Dump the monster attr/char info */
                fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n",
                        i, kv, rv, gv, bv);
            }

            /* All done */
            fprintf(fff, "\n\n\n\n");

            /* Close */
            my_fclose(fff);

            /* Message */
            msg_print("Dumped color redefinitions.");
        }

        /* Edit colors */
        else if (i == '3') {

            static int a = 0;

            /* Prompt */
            prt("Command: Modify colors", 8, 0);

            /* Hack -- query until done */
            while (1) {

                cptr name;
                
                /* Clear */
                clear_from(10);
                
                /* Exhibit the normal colors */
                for (i = 0; i < 16; i++) {

                    /* Exhibit this color */
                    Term_putstr(i*4, 20, -1, a, "###");

                    /* Exhibit all colors */
                    Term_putstr(i*4, 22, -1, i, format("%3d", i));
                }

                /* Describe the color */
                name = ((a < 16) ? color_names[a] : "undefined");

                /* Describe the color */
                Term_putstr(5, 10, -1, TERM_WHITE,
                            format("Color = %d, Name = %s", a, name));

                /* Label the Current values */
                Term_putstr(5, 12, -1, TERM_WHITE,
                            format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
                                   color_table[a][0], color_table[a][1],
                                   color_table[a][2], color_table[a][3]));

                /* Prompt */
                Term_putstr(0, 14, -1, TERM_WHITE,
                            "Command (n/N/k/K/r/R/g/G/b/B): ");

                /* Get a command */
                i = inkey();

                /* All done */
                if (i == ESCAPE) break;

                /* Analyze */
                if (i == 'n') a = (byte)(a + 1);
                if (i == 'N') a = (byte)(a - 1);
                if (i == 'k') color_table[a][0] = (byte)(color_table[a][0] + 1);
                if (i == 'K') color_table[a][0] = (byte)(color_table[a][0] - 1);
                if (i == 'r') color_table[a][1] = (byte)(color_table[a][1] + 1);
                if (i == 'R') color_table[a][1] = (byte)(color_table[a][1] - 1);
                if (i == 'g') color_table[a][2] = (byte)(color_table[a][2] + 1);
                if (i == 'G') color_table[a][2] = (byte)(color_table[a][2] - 1);
                if (i == 'b') color_table[a][3] = (byte)(color_table[a][3] + 1);
                if (i == 'B') color_table[a][3] = (byte)(color_table[a][3] - 1);
                
                /* Hack -- react to changes */
                Term_xtra(TERM_XTRA_REACT, 0);

                /* Hack -- redraw */
                Term_redraw();
            }
        }

#endif /* ANGBAND_LITE */

        /* Unknown option */
        else {
            bell();
        }
        
        /* Flush messages */
        msg_print(NULL);
    }


    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}


/*
 * Note something in the message recall
 */
void do_cmd_note(void)
{
    char buf[80];

    /* Get a "note" */
    prt("Note: ", 0, 0);
    if (!askfor(buf, 60)) return;

    /* Add the note to the message recall */
    msg_format("Note: %s", buf);
}


/*
 * Mention the current version
 */
void do_cmd_version(void)
{
    /* Silly message */
    msg_format("You are playing Angband %d.%d.%d.  Type '?' for more info.",
               VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
}



/*
 * Note that "feeling" is set to zero unless some time has passed.
 * Note that this is done when the level is GENERATED, not entered.
 */
void do_cmd_feeling()
{
    if (complex_feelings) {
        if (f_buf[0] != '\0') msg_print(f_buf);
        else msg_print("You feel nothing.");
    }
    else {
        /* No useful feeling in town */
        if (!dun_level) {
            msg_print("Looks like a typical town.");
            return;
            }
    
        /* Analyze the feeling */
        switch (feeling) {
          case 0:
            msg_print("Looks like any other level.");
            break;
          case 1:
            msg_print("You feel there is something special about this level.");
            break;
          case 2:
            msg_print("You have a superb feeling about this level.");
            break;
          case 3:
            msg_print("You have an excellent feeling...");
            break;
          case 4:
            msg_print("You have a very good feeling...");
            break;
          case 5:
            msg_print("You have a good feeling...");
            break;
          case 6:
            msg_print("You feel strangely lucky...");
            break;
          case 7:
            msg_print("You feel your luck is turning...");
            break;
          case 8:
            msg_print("You like the look of this place...");
            break;
          case 9:
            msg_print("This level can't be all bad...");
            break;
          default:
            msg_print("What a boring place...");
            break;
        }
    }
}





#ifndef ANGBAND_LITE


/*
 * Encode the screen colors
 */
static char hack[17] = "dwsorgbuDWvyRGBU";


/*
 * Hack -- load a screen dump from a file
 */
void do_cmd_load_screen(void)
{
    int i, y, x;

    byte a = 0;
    char c = ' ';

    bool okay = TRUE;

    FILE *fff;

    char buf[1024];


    /* Build the path name */
    strcpy(buf, ANGBAND_DIR_USER);
    strcat(buf, "dump.txt");

    /* Append to the file */
    fff = my_fopen(buf, "r");

    /* Oops */
    if (!fff) return;


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();
    
    /* Clear the screen */
    Term_clear();

    
    /* Load the screen */
    for (y = 0; okay && (y < 24); y++) {

        /* Get a line of data */
        if (my_fgets(fff, buf, 1024)) okay = FALSE;
        
        /* Show each row */
        for (x = 0; x < 79; x++) {

            /* Put the attr/char */
            Term_draw(x, y, TERM_WHITE, buf[x]);
        }
    }

    /* Get the blank line */
    if (my_fgets(fff, buf, 1024)) okay = FALSE;


    /* Dump the screen */
    for (y = 0; okay && (y < 24); y++) {

        /* Get a line of data */
        if (my_fgets(fff, buf, 1024)) okay = FALSE;
        
        /* Dump each row */
        for (x = 0; x < 79; x++) {

            /* Get the attr/char */
            (void)(Term_what(x, y, &a, &c));

            /* Look up the attr */
            for (i = 0; i < 16; i++) {
            
                /* Use attr matches */
                if (hack[i] == buf[x]) a = i;
            }

            /* Put the attr/char */
            Term_draw(x, y, a, c);
        }
            
        /* End the row */
        fprintf(fff, "\n");
    }

    /* Get the blank line */
    if (my_fgets(fff, buf, 1024)) okay = FALSE;


    /* Close it */
    my_fclose(fff);


    /* Message */
    msg_print("Screen dump loaded.");
    msg_print(NULL);


    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}


/*
 * Hack -- save a screen dump to a file
 */
void do_cmd_save_screen(void)
{
    int y, x;

    byte a = 0;
    char c = ' ';
    
    FILE *fff;

    char buf[1024];


    /* Build the path name */
    strcpy(buf, ANGBAND_DIR_USER);
    strcat(buf, "dump.txt");

#if defined(MACINTOSH) && !defined(applec)
    _ftype = 'TEXT';
#endif

    /* Hack -- drop permissions */
    safe_setuid_drop();

    /* Append to the file */
    fff = my_fopen(buf, "w");

    /* Hack -- grab permissions */
    safe_setuid_grab();

    /* Oops */
    if (!fff) return;


    /* Enter "icky" mode */
    character_icky = TRUE;

    /* Save the screen */
    Term_save();
    

    /* Dump the screen */
    for (y = 0; y < 24; y++) {

        /* Dump each row */
        for (x = 0; x < 79; x++) {

            /* Get the attr/char */
            (void)(Term_what(x, y, &a, &c));

            /* Dump it */
            buf[x] = c;
        }

        /* Terminate */
        buf[x] = '\0';
        
        /* End the row */
        fprintf(fff, "%s\n", buf);
    }

    /* Skip a line */
    fprintf(fff, "\n");


    /* Dump the screen */
    for (y = 0; y < 24; y++) {

        /* Dump each row */
        for (x = 0; x < 79; x++) {

            /* Get the attr/char */
            (void)(Term_what(x, y, &a, &c));

            /* Dump it */
            buf[x] = hack[a&0x0F];
        }
        
        /* Terminate */
        buf[x] = '\0';
        
        /* End the row */
        fprintf(fff, "%s\n", buf);
    }

    /* Skip a line */
    fprintf(fff, "\n");


    /* Close it */
    my_fclose(fff);


    /* Message */
    msg_print("Screen dump saved.");
    msg_print(NULL);


    /* Restore the screen */
    Term_load();

    /* Leave "icky" mode */
    character_icky = FALSE;
}


#endif




/*
 * Check the status of "artifacts"
 *
 * XXX Consider use of "term_mirror"
 */
void do_cmd_check_artifacts(void)
{
    int i, k, z, x, y;

    FILE *fff;

    char file_name[1024];

    char base_name[80];

    bool okay[MAX_A_IDX];


    /* Temporary file */
    if (path_temp(file_name, 1024)) return;

    /* Open a new file */
    fff = fopen(file_name, "w");

    /* Scan the artifacts */
    for (k = 0; k < MAX_A_IDX; k++) {

        artifact_type *a_ptr = &a_info[k];

        /* Default */
        okay[k] = FALSE;

        /* Skip "empty" artifacts */
        if (!a_ptr->name) continue;

        /* Skip "uncreated" artifacts */
        if (!a_ptr->cur_num) continue;

        /* Assume okay */
        okay[k] = TRUE;
    }

    /* Check the dungeon */
    for (y = 0; y < cur_hgt; y++) {
        for (x = 0; x < cur_wid; x++) {

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

            /* Process objects */
            if (c_ptr->i_idx) {

                inven_type *i_ptr = &i_list[c_ptr->i_idx];

                /* Ignore non-artifacts */
                if (!artifact_p(i_ptr)) continue;

                /* Ignore known items */
                if (inven_known_p(i_ptr)) continue;
                    
                /* Note the artifact */
                okay[i_ptr->name1] = FALSE;
            }
        }
    }

    /* Check the inventory */
    for (i = 0; i < INVEN_PACK; i++) {

        inven_type *i_ptr = &inventory[i];

        /* Ignore non-objects */
        if (!i_ptr->k_idx) continue;

        /* Ignore non-artifacts */
        if (!artifact_p(i_ptr)) continue;

        /* Ignore known items */
        if (inven_known_p(i_ptr)) continue;
                    
        /* Note the artifact */
        okay[i_ptr->name1] = FALSE;
    }

    /* Scan the artifacts */
    for (k = 0; k < MAX_A_IDX; k++) {

        artifact_type *a_ptr = &a_info[k];
        
        /* List "dead" ones */
        if (!okay[k]) continue;
       
        /* Paranoia */
        strcpy(base_name, "Unknown Artifact");

        /* Obtain the base object type */
        z = lookup_kind(a_ptr->tval, a_ptr->sval);

        /* Real object */
        if (z) {
        
            inven_type forge;

            /* Create the object */
            invcopy(&forge, z);

            /* Create the artifact */
            forge.name1 = k;

            /* Describe the artifact */
            objdes_store(base_name, &forge, FALSE, 0);
        }

        /* Hack -- Build the artifact name */
        fprintf(fff, "     The %s\n", base_name);
    }

    /* Close the file */
    fclose(fff);

    /* Display the file contents */
    show_file(file_name, "Artifacts Seen");
    
    /* Remove the file */
    remove(file_name);
}


/*
 * Check the status of "uniques"
 *
 * XXX Consider use of "term_mirror"
 */
void do_cmd_check_uniques()
{
    int k;

    FILE *fff;

    char file_name[1024];


    /* Temporary file */
    if (path_temp(file_name, 1024)) return;

    /* Open a new file */
    fff = fopen(file_name, "w");

    /* Note -- skip the ghost */
    for (k = 1; k < MAX_R_IDX-1; k++) {

        monster_race *r_ptr = &r_info[k];

        /* Only print Uniques */
        if (r_ptr->flags1 & RF1_UNIQUE) {

            bool dead = (r_ptr->max_num == 0);

            /* Only display "known" uniques */
            if (dead || cheat_know || r_ptr->r_sights) {

                /* Print a message */
                fprintf(fff, "     %s is %s\n",
                        (r_name + r_ptr->name),
                        (dead ? "dead" : "alive"));
            }
        }
    }

    /* Close the file */
    fclose(fff);

    /* Display the file contents */
    show_file(file_name, "Known Uniques");
    
    /* Remove the file */
    remove(file_name);
}



