/* NetHack 3.6	end.c	$NHDT-Date: 1448241780 2015/11/23 01:23:00 $  $NHDT-Branch: master $:$NHDT-Revision: 1.108 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#define NEED_VARARGS /* comment line for pre-compiled headers */

#include "hack.h"
#include "lev.h"
#ifndef NO_SIGNAL
#include <signal.h>
#endif
#include <ctype.h>
#include <limits.h>
#include "dlb.h"

/* add b to long a, convert wraparound to max value */
#define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))

/* these probably ought to be generated by makedefs, like LAST_GEM */
#define FIRST_GEM DILITHIUM_CRYSTAL
#define FIRST_AMULET AMULET_OF_ESP
#define LAST_AMULET AMULET_OF_YENDOR

struct valuable_data {
    long count;
    int typ;
};

static struct valuable_data
    gems[LAST_GEM + 1 - FIRST_GEM + 1], /* 1 extra for glass */
    amulets[LAST_AMULET + 1 - FIRST_AMULET];

static struct val_list {
    struct valuable_data *list;
    int size;
} valuables[] = { { gems, sizeof gems / sizeof *gems },
                  { amulets, sizeof amulets / sizeof *amulets },
                  { 0, 0 } };

#ifndef NO_SIGNAL
STATIC_PTR void FDECL(done_intr, (int));
#if defined(UNIX) || defined(VMS) || defined(__EMX__)
static void FDECL(done_hangup, (int));
#endif
#endif
STATIC_DCL void FDECL(disclose, (int, BOOLEAN_P));
STATIC_DCL void FDECL(get_valuables, (struct obj *));
STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int));
STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
STATIC_DCL void FDECL(really_done, (int)) NORETURN;
STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
STATIC_DCL void FDECL(savelife, (int));
STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *));
STATIC_DCL int NDECL(num_extinct);

#if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
extern void FDECL(nethack_exit, (int));
#else
#define nethack_exit exit
#endif

#define done_stopprint program_state.stopprint

#ifndef PANICTRACE
#define NH_abort NH_abort_
#endif

#ifdef AMIGA
#define NH_abort_() Abort(0)
#else
#ifdef SYSV
#define NH_abort_() (void) abort()
#else
#ifdef WIN32
#define NH_abort_() win32_abort()
#else
#define NH_abort_() abort()
#endif
#endif /* !SYSV */
#endif /* !AMIGA */

#ifdef PANICTRACE
#include <errno.h>
#ifdef PANICTRACE_LIBC
#include <execinfo.h>
#endif

/* What do we try and in what order?  Tradeoffs:
 * libc: +no external programs required
 *        -requires newish libc/glibc
 *        -requires -rdynamic
 * gdb:   +gives more detailed information
 *        +works on more OS versions
 *        -requires -g, which may preclude -O on some compilers
 */
#ifdef SYSCF
#define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
#ifdef PANICTRACE_LIBC
#define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
#else
#define SYSOPT_PANICTRACE_LIBC 0
#endif
#else
#define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
#ifdef PANICTRACE_LIBC
#define SYSOPT_PANICTRACE_LIBC 1
#else
#define SYSOPT_PANICTRACE_LIBC 0
#endif
#endif

static void NDECL(NH_abort);
#ifndef NO_SIGNAL
static void FDECL(panictrace_handler, (int));
#endif
static boolean NDECL(NH_panictrace_libc);
static boolean NDECL(NH_panictrace_gdb);

#ifndef NO_SIGNAL
/*ARGSUSED*/
void panictrace_handler(
    sig_unused) /* called as signal() handler, so sent at least one arg */
int sig_unused UNUSED;
{
#define SIG_MSG "\nSignal received.\n"
    (void) write(2, SIG_MSG, sizeof(SIG_MSG) - 1);
    NH_abort();
}

void
panictrace_setsignals(set)
boolean set;
{
#define SETSIGNAL(sig) \
    (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
#ifdef SIGILL
    SETSIGNAL(SIGILL);
#endif
#ifdef SIGTRAP
    SETSIGNAL(SIGTRAP);
#endif
#ifdef SIGIOT
    SETSIGNAL(SIGIOT);
#endif
#ifdef SIGBUS
    SETSIGNAL(SIGBUS);
#endif
#ifdef SIGFPE
    SETSIGNAL(SIGFPE);
#endif
#ifdef SIGSEGV
    SETSIGNAL(SIGSEGV);
#endif
#ifdef SIGSTKFLT
    SETSIGNAL(SIGSTKFLT);
#endif
#ifdef SIGSYS
    SETSIGNAL(SIGSYS);
#endif
#ifdef SIGEMT
    SETSIGNAL(SIGEMT);
#endif
#undef SETSIGNAL
}
#endif /* NO_SIGNAL */

static void
NH_abort()
{
    int gdb_prio = SYSOPT_PANICTRACE_GDB;
    int libc_prio = SYSOPT_PANICTRACE_LIBC;
    static boolean aborting = FALSE;

    if (aborting)
        return;
    aborting = TRUE;

#ifndef VMS
    if (gdb_prio == libc_prio && gdb_prio > 0)
        gdb_prio++;

    if (gdb_prio > libc_prio) {
        NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc());
    } else {
        NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb());
    }

#else /* VMS */
    /* overload otherwise unused priority for debug mode: 1 = show
       traceback and exit; 2 = show traceback and stay in debugger */
    /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
    vms_traceback(gdb_prio);
    (void) libc_prio; /* half-hearted attempt at lint suppression */

#endif /* ?VMS */

#ifndef NO_SIGNAL
    panictrace_setsignals(FALSE);
#endif
    NH_abort_();
}

static boolean
NH_panictrace_libc()
{
#ifdef PANICTRACE_LIBC
    void *bt[20];
    size_t count, x;
    char **info;

    raw_print("Generating more information you may report:\n");
    count = backtrace(bt, SIZE(bt));
    info = backtrace_symbols(bt, count);
    for (x = 0; x < count; x++) {
        raw_printf("[%lu] %s", (unsigned long) x, info[x]);
    }
    /* free(info);   -- Don't risk it. */
    return TRUE;
#else
    return FALSE;
#endif /* !PANICTRACE_LIBC */
}

/*
 *   fooPATH  file system path for foo
 *   fooVAR   (possibly const) variable containing fooPATH
 */
#ifdef PANICTRACE_GDB
#ifdef SYSCF
#define GDBVAR sysopt.gdbpath
#define GREPVAR sysopt.greppath
#else /* SYSCF */
#define GDBVAR GDBPATH
#define GREPVAR GREPPATH
#endif /* SYSCF */
#endif /* PANICTRACE_GDB */

static boolean
NH_panictrace_gdb()
{
#ifdef PANICTRACE_GDB
    /* A (more) generic method to get a stack trace - invoke
     * gdb on ourself. */
    char *gdbpath = GDBVAR;
    char *greppath = GREPVAR;
    char buf[BUFSZ];
    FILE *gdb;

    if (gdbpath == NULL || gdbpath[0] == 0)
        return FALSE;
    if (greppath == NULL || greppath[0] == 0)
        return FALSE;

    sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath, ARGV0, getpid(),
            greppath);
    gdb = popen(buf, "w");
    if (gdb) {
        raw_print("Generating more information you may report:\n");
        fprintf(gdb, "bt\nquit\ny");
        fflush(gdb);
        sleep(4); /* ugly */
        pclose(gdb);
        return TRUE;
    } else {
        return FALSE;
    }
#else
    return FALSE;
#endif /* !PANICTRACE_GDB */
}
#endif /* PANICTRACE */

#if 0 /*JP*//*日本語では使わない*/
/*
 * The order of these needs to match the macros in hack.h.
 */
static NEARDATA const char *deaths[] = {
    /* the array of death */
    "died", "choked", "poisoned", "starvation", "drowning", "burning",
    "dissolving under the heat and pressure", "crushed", "turned to stone",
    "turned into slime", "genocided", "panic", "trickery", "quit",
    "escaped", "ascended"
};
#endif /*JP*/

static NEARDATA const char *ends[] = {
    /* "when you %s" */
#if 0 /*JP*/
    "died", "choked", "were poisoned",
    "starved", "drowned", "burned",
    "dissolved in the lava",
    "were crushed", "turned to stone",
    "turned into slime", "were genocided",
    "panicked", "were tricked", "quit",
    "escaped", "ascended"
#else /*JP: 最後に「殺された」追加 */
    "死んだ", "窒息した", "毒におかされた",
    "餓死した", "溺死した", "焼死した",
    "溶岩に溶けた",
    "押し潰された", "石になった",
    "どろどろに溶けた", "虐殺された",
    "パニックにおちいった", "奇妙な出来事に会った", "抜けた",
    "脱出した", "昇天した", "殺された"
#endif
};

static boolean Schroedingers_cat = FALSE;

/*ARGSUSED*/
void
done1(sig_unused) /* called as signal() handler, so sent at least one arg */
int sig_unused UNUSED;
{
#ifndef NO_SIGNAL
    (void) signal(SIGINT, SIG_IGN);
#endif
    if (flags.ignintr) {
#ifndef NO_SIGNAL
        (void) signal(SIGINT, (SIG_RET_TYPE) done1);
#endif
        clear_nhwindow(WIN_MESSAGE);
        curs_on_u();
        wait_synch();
        if (multi > 0)
            nomul(0);
    } else {
        (void) done2();
    }
}

/* "#quit" command or keyboard interrupt */
int
done2()
{
/*JP
    if (!paranoid_query(ParanoidQuit, "Really quit?")) {
*/
    if (!paranoid_query(ParanoidQuit, "本当にやめる？")) {
#ifndef NO_SIGNAL
        (void) signal(SIGINT, (SIG_RET_TYPE) done1);
#endif
        clear_nhwindow(WIN_MESSAGE);
        curs_on_u();
        wait_synch();
        if (multi > 0)
            nomul(0);
        if (multi == 0) {
            u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
            u.usleep = 0;
        }
        return 0;
    }
#if (defined(UNIX) || defined(VMS) || defined(LATTICE))
    if (wizard) {
        int c;
#ifdef VMS
        extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */

        c = !debuggable ? 'n' : ynq("Enter debugger?");
#else
#ifdef LATTICE
        c = ynq("Create SnapShot?");
#else
        c = ynq("Dump core?");
#endif
#endif
        if (c == 'y') {
#ifndef NO_SIGNAL
            (void) signal(SIGINT, (SIG_RET_TYPE) done1);
#endif
            exit_nhwindows((char *) 0);
            NH_abort();
        } else if (c == 'q')
            done_stopprint++;
    }
#endif
#ifndef LINT
    done(QUIT);
#endif
    return 0;
}

#ifndef NO_SIGNAL
/*ARGSUSED*/
STATIC_PTR void
done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
int sig_unused UNUSED;
{
    done_stopprint++;
    (void) signal(SIGINT, SIG_IGN);
#if defined(UNIX) || defined(VMS)
    (void) signal(SIGQUIT, SIG_IGN);
#endif
    return;
}

#if defined(UNIX) || defined(VMS) || defined(__EMX__)
/* signal() handler */
static void
done_hangup(sig)
int sig;
{
    program_state.done_hup++;
    sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
    done_intr(sig);
    return;
}
#endif
#endif /* NO_SIGNAL */

void
done_in_by(mtmp, how)
struct monst *mtmp;
int how;
{
    char buf[BUFSZ];
    struct permonst *mptr = mtmp->data,
                    *champtr = ((mtmp->cham >= LOW_PM)
                                   ? &mons[mtmp->cham]
                                   : mptr);
    boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
            mimicker = (mtmp->m_ap_type == M_AP_MONSTER),
            imitator = (mptr != champtr || mimicker);

/*JP
    You((how == STONING) ? "turn to stone..." : "die...");
*/
    You((how == STONING) ? "石になった．．．" : "死にました．．．");
    mark_synch(); /* flush buffered screen output */
    buf[0] = '\0';
    killer.format = KILLED_BY_AN;
#if 0 /*JP*//* 日本語には関係ないのでまとめてコメントアウト */
    /* "killed by the high priest of Crom" is okay,
       "killed by the high priest" alone isn't */
    if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
        && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
        if (!type_is_pname(mptr))
            Strcat(buf, "the ");
        killer.format = KILLED_BY;
    }
    /* _the_ <invisible> <distorted> ghost of Dudley */
    if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
        Strcat(buf, "the ");
        killer.format = KILLED_BY;
    }
#endif
    if (mtmp->minvis)
/*JP
        Strcat(buf, "invisible ");
*/
        Strcat(buf, "透明な");
    if (distorted)
/*JP
        Strcat(buf, "hallucinogen-distorted ");
*/
        Strcat(buf, "幻覚で歪んだ");

    if (imitator) {
        char shape[BUFSZ];
        const char *realnm = champtr->mname, *fakenm = mptr->mname;
        boolean alt = is_vampshifter(mtmp);

        if (mimicker) {
            /* realnm is already correct because champtr==mptr;
               set up fake mptr for type_is_pname/the_unique_pm */
            mptr = &mons[mtmp->mappearance];
            fakenm = mptr->mname;
#if 0 /*JP*/
        } else if (alt && strstri(realnm, "vampire")
                   && !strcmp(fakenm, "vampire bat")) {
            /* special case: use "vampire in bat form" in preference
               to redundant looking "vampire in vampire bat form" */
            fakenm = "bat";
#else
        } else if (alt && strstri(realnm, "吸血鬼")
                   && !strcmp(fakenm, "吸血こうもり")) {
            /* 「吸血こうもりの姿の吸血鬼」は冗長なので
               「こうもりの姿の吸血鬼」の形にする */
            fakenm = "こうもり";
#endif
        }
#if 0 /*JP*/
        /* for the alternate format, always suppress any article;
           pname and the_unique should also have s_suffix() applied,
           but vampires don't take on any shapes which warrant that */
        if (alt || type_is_pname(mptr)) /* no article */
            Strcpy(shape, fakenm);
        else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
            Sprintf(shape, "the %s", fakenm);
        else /* "a"/"an" */
            Strcpy(shape, an(fakenm));
#else /*JP:日本語ではシンプル*/
        Strcpy(shape, fakenm);
#endif
        /* omit "called" to avoid excessive verbosity */
#if 0 /*JP*/
        Sprintf(eos(buf),
                alt ? "%s in %s form"
                    : mimicker ? "%s disguised as %s"
                               : "%s imitating %s",
                realnm, shape);
#else
        Sprintf(eos(buf),
                alt ? "%sの姿の%s"
                    : mimicker ? "%sのふりをしている%s"
                               : "%sのまねをしている%s",
                shape, realnm);
#endif
        mptr = mtmp->data; /* reset for mimicker case */
    } else if (mptr == &mons[PM_GHOST]) {
#if 0 /*JP*/
        Strcat(buf, "ghost");
        if (has_mname(mtmp))
            Sprintf(eos(buf), " of %s", MNAME(mtmp));
#else
        if (has_mname(mtmp))
            Sprintf(eos(buf), "%sの幽霊", MNAME(mtmp));
        else
            Strcat(buf, "幽霊");
#endif
    } else if (mtmp->isshk) {
#if 0 /*JP*/
        const char *shknm = shkname(mtmp),
                   *honorific = shkname_is_pname(mtmp) ? ""
                                   : mtmp->female ? "Ms. " : "Mr. ";

        Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
#else
        Sprintf(eos(buf), "%sという名の店主", shkname(mtmp));
#endif
        killer.format = KILLED_BY;
    } else if (mtmp->ispriest || mtmp->isminion) {
        /* m_monnam() suppresses "the" prefix plus "invisible", and
           it overrides the effect of Hallucination on priestname() */
        Strcat(buf, m_monnam(mtmp));
    } else {
#if 0 /*JP*/
        Strcat(buf, mptr->mname);
        if (has_mname(mtmp))
            Sprintf(eos(buf), " called %s", MNAME(mtmp));
#else
        Strcat(buf, mptr->mname);
#endif
    }

    Strcpy(killer.name, buf);
    if (mptr->mlet == S_WRAITH)
        u.ugrave_arise = PM_WRAITH;
    else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
        u.ugrave_arise = urace.mummynum;
    else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
        u.ugrave_arise = PM_VAMPIRE;
    else if (mptr == &mons[PM_GHOUL])
        u.ugrave_arise = PM_GHOUL;
    /* this could happen if a high-end vampire kills the hero
       when ordinary vampires are genocided; ditto for wraiths */
    if (u.ugrave_arise >= LOW_PM
        && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
        u.ugrave_arise = NON_PM;

#if 1 /*JP*/
    if (how == STONING){
        /*JP
              topten.c の killed_by_prefix を参照のこと。
              STONING の場合は "石化した" が補われる。
         */
        Strcat(buf, "の攻撃で");
    }
    if (how == DIED){
        /*JP
              DIED の場合は通常 "死んだ" が補われるが、
              怪物による場合は "に殺された" を補う。
         */
        killer.format = KILLED_SUFFIX;
        done(DIED);
    }
#endif
    done(how);
    return;
}

#if defined(WIN32) && !defined(SYSCF)
#define NOTIFY_NETHACK_BUGS
#endif

/*VARARGS1*/
void panic
VA_DECL(const char *, str)
{
    VA_START(str);
    VA_INIT(str, char *);

    if (program_state.panicking++)
        NH_abort(); /* avoid loops - this should never happen*/

    if (iflags.window_inited) {
        raw_print("\r\nOops...");
        wait_synch(); /* make sure all pending output gets flushed */
        exit_nhwindows((char *) 0);
        iflags.window_inited = 0; /* they're gone; force raw_print()ing */
    }

    raw_print(program_state.gameover
/*JP
                  ? "Postgame wrapup disrupted."
*/
                  ? "ゲーム終了時の処理が崩壊した．"
                  : !program_state.something_worth_saving
/*JP
                        ? "Program initialization has failed."
*/
                        ? "プログラムの初期化に失敗した．"
/*JP
                        : "Suddenly, the dungeon collapses.");
*/
                        : "突然迷宮が崩れた．");
#ifndef MICRO
#if defined(NOTIFY_NETHACK_BUGS)
    if (!wizard)
        raw_printf("Report the following error to \"%s\" or at \"%s\".",
                   DEVTEAM_EMAIL, DEVTEAM_URL);
    else if (program_state.something_worth_saving)
        raw_print("\nError save file being written.\n");
#else
    if (!wizard) {
#if 0 /*JP*/
        const char *maybe_rebuild = !program_state.something_worth_saving
                                     ? "."
                                     : "\nand it may be possible to rebuild.";
#else
        const char *maybe_rebuild = !program_state.something_worth_saving
                                     ? "．"
                                     : "\n復旧できる可能性があります．";
#endif

        if (sysopt.support)
            raw_printf("To report this error, %s%s", sysopt.support,
                       maybe_rebuild);
        else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
            raw_printf("To report this error, contact %s%s",
                       sysopt.fmtd_wizard_list, maybe_rebuild);
        else
#if 0 /*JP*/
            raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
                       maybe_rebuild);
#else
            raw_printf("\"%s\"にエラーを報告してください．%s", WIZARD_NAME,
                       maybe_rebuild);
#endif
    }
#endif
    /* XXX can we move this above the prints?  Then we'd be able to
     * suppress "it may be possible to rebuild" based on dosave0()
     * or say it's NOT possible to rebuild. */
    if (program_state.something_worth_saving) {
        set_error_savefile();
        if (dosave0()) {
            /* os/win port specific recover instructions */
            if (sysopt.recover)
                raw_printf("%s", sysopt.recover);
        }
    }
#endif
    {
        char buf[BUFSZ];

        Vsprintf(buf, str, VA_ARGS);
        raw_print(buf);
        paniclog("panic", buf);
    }
#ifdef WIN32
    interject(INTERJECT_PANIC);
#endif
#if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
    if (wizard)
        NH_abort(); /* generate core dump */
#endif
    VA_END();
    really_done(PANICKED);
}

STATIC_OVL boolean
should_query_disclose_option(category, defquery)
int category;
char *defquery;
{
    int idx;
    char *dop;

    *defquery = 'n';
    if ((dop = index(disclosure_options, category)) != 0) {
        idx = (int) (dop - disclosure_options);
        if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
            impossible(
                   "should_query_disclose_option: bad disclosure index %d %c",
                       idx, category);
            *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
            return TRUE;
        }
        if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) {
            *defquery = 'y';
            return FALSE;
        } else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) {
            *defquery = 'n';
            return FALSE;
        } else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) {
            *defquery = 'y';
            return TRUE;
        } else {
            *defquery = 'n';
            return TRUE;
        }
    }
    impossible("should_query_disclose_option: bad category %c", category);
    return TRUE;
}

STATIC_OVL void
disclose(how, taken)
int how;
boolean taken;
{
    char c = '\0', defquery;
    char qbuf[QBUFSZ];
    boolean ask = FALSE;

    if (invent && !done_stopprint) {
        if (taken)
#if 0 /*JP*/
            Sprintf(qbuf, "Do you want to see what you had when you %s?",
                    (how == QUIT) ? "quit" : "died");
#else
            Sprintf(qbuf,"%sとき何を持っていたか見ますか？",
                    (how == QUIT) ? "やめた" : "死んだ");
#endif
        else
/*JP
            Strcpy(qbuf, "Do you want your possessions identified?");
*/
            Strcpy(qbuf,"持ち物を識別しますか？"); 

        ask = should_query_disclose_option('i', &defquery);
        c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
        if (c == 'y') {
            struct obj *obj;

            for (obj = invent; obj; obj = obj->nobj) {
                makeknown(obj->otyp);
                obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
                if (Is_container(obj) || obj->otyp == STATUE)
                    obj->cknown = obj->lknown = 1;
            }
            (void) display_inventory((char *) 0, TRUE);
            container_contents(invent, TRUE, TRUE, FALSE);
        }
        if (c == 'q')
            done_stopprint++;
    }

    if (!done_stopprint) {
        ask = should_query_disclose_option('a', &defquery);
#if 0 /*JP*/
        c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
                              defquery)
                : defquery;
#else
        c = ask ? yn_function("属性を見ますか？", ynqchars,
                              defquery)
                : defquery;
#endif
        if (c == 'y')
            enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
                          (how >= PANICKED) ? ENL_GAMEOVERALIVE
                                            : ENL_GAMEOVERDEAD);
        if (c == 'q')
            done_stopprint++;
    }

    if (!done_stopprint) {
        ask = should_query_disclose_option('v', &defquery);
        list_vanquished(defquery, ask);
    }

    if (!done_stopprint) {
        ask = should_query_disclose_option('g', &defquery);
        list_genocided(defquery, ask);
    }

    if (!done_stopprint) {
        ask = should_query_disclose_option('c', &defquery);
#if 0 /*JP*/
        c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
                              defquery)
                : defquery;
#else
        c = ask ? yn_function("どういう行動をとったか見ますか？", ynqchars,
                              defquery)
                : defquery;
#endif
        if (c == 'y')
            show_conduct((how >= PANICKED) ? 1 : 2);
        if (c == 'q')
            done_stopprint++;
    }

    if (!done_stopprint) {
        ask = should_query_disclose_option('o', &defquery);
#if 0 /*JP*/
        c = ask ? yn_function("Do you want to see the dungeon overview?",
                              ynqchars, defquery)
                : defquery;
#else
        c = ask ? yn_function("迷宮の概要を見ますか？",
                              ynqchars, defquery)
                : defquery;
#endif
        if (c == 'y')
            show_overview((how >= PANICKED) ? 1 : 2, how);
        if (c == 'q')
            done_stopprint++;
    }
}

/* try to get the player back in a viable state after being killed */
STATIC_OVL void
savelife(how)
int how;
{
    int uhpmin = max(2 * u.ulevel, 10);

    if (u.uhpmax < uhpmin)
        u.uhpmax = uhpmin;
    u.uhp = u.uhpmax;
    u.uswldtim = 0;
    if (u.uhunger < 500) {
        u.uhunger = 500;
        newuhs(FALSE);
    }
    /* cure impending doom of sickness hero won't have time to fix */
    if ((Sick & TIMEOUT) == 1L) {
        u.usick_type = 0;
        set_itimeout(&Sick, 0L);
    }
    if (how == CHOKING)
        init_uhunger();
/*JP
    nomovemsg = "You survived that attempt on your life.";
*/
    nomovemsg = "あなたは生きながらえた．";
    context.move = 0;
    if (multi > 0)
        multi = 0;
    else
        multi = -1;
    if (u.utrap && u.utraptype == TT_LAVA)
        u.utrap = 0;
    context.botl = 1;
    u.ugrave_arise = NON_PM;
    HUnchanging = 0L;
    curs_on_u();
}

/*
 * Get valuables from the given list.  Revised code: the list always remains
 * intact.
 */
STATIC_OVL void
get_valuables(list)
struct obj *list; /* inventory or container contents */
{
    register struct obj *obj;
    register int i;

    /* find amulets and gems, ignoring all artifacts */
    for (obj = list; obj; obj = obj->nobj)
        if (Has_contents(obj)) {
            get_valuables(obj->cobj);
        } else if (obj->oartifact) {
            continue;
        } else if (obj->oclass == AMULET_CLASS) {
            i = obj->otyp - FIRST_AMULET;
            if (!amulets[i].count) {
                amulets[i].count = obj->quan;
                amulets[i].typ = obj->otyp;
            } else
                amulets[i].count += obj->quan; /* always adds one */
        } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
            i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
            if (!gems[i].count) {
                gems[i].count = obj->quan;
                gems[i].typ = obj->otyp;
            } else
                gems[i].count += obj->quan;
        }
    return;
}

/*
 *  Sort collected valuables, most frequent to least.  We could just
 *  as easily use qsort, but we don't care about efficiency here.
 */
STATIC_OVL void
sort_valuables(list, size)
struct valuable_data list[];
int size; /* max value is less than 20 */
{
    register int i, j;
    struct valuable_data ltmp;

    /* move greater quantities to the front of the list */
    for (i = 1; i < size; i++) {
        if (list[i].count == 0)
            continue;   /* empty slot */
        ltmp = list[i]; /* structure copy */
        for (j = i; j > 0; --j)
            if (list[j - 1].count >= ltmp.count)
                break;
            else {
                list[j] = list[j - 1];
            }
        list[j] = ltmp;
    }
    return;
}

#define CAT_CHECK 2

STATIC_OVL boolean
odds_and_ends(list, what)
struct obj *list;
int what;
{
    struct obj *otmp;
    for (otmp = list; otmp; otmp = otmp->nobj) {
        switch (what) {
        case CAT_CHECK: /* Schroedinger's Cat */
            /* Ascending is deterministic */
            if (SchroedingersBox(otmp))
                return rn2(2);
            break;
        }
        if (Has_contents(otmp))
            return odds_and_ends(otmp->cobj, what);
    }
    return FALSE;
}

/* called twice; first to calculate total, then to list relevant items */
STATIC_OVL void
artifact_score(list, counting, endwin)
struct obj *list;
boolean counting; /* true => add up points; false => display them */
winid endwin;
{
    char pbuf[BUFSZ];
    struct obj *otmp;
    long value, points;
    short dummy; /* object type returned by artifact_name() */

    for (otmp = list; otmp; otmp = otmp->nobj) {
        if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
            || otmp->otyp == SPE_BOOK_OF_THE_DEAD
            || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
            value = arti_cost(otmp); /* zorkmid value */
            points = value * 5 / 2;  /* score value */
            if (counting) {
                nowrap_add(u.urexp, points);
            } else {
                makeknown(otmp->otyp);
                otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
                /* assumes artifacts don't have quan > 1 */
#if 0 /*JP*/
                Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
                        the_unique_obj(otmp) ? "The " : "",
                        otmp->oartifact ? artifact_name(xname(otmp), &dummy)
                                        : OBJ_NAME(objects[otmp->otyp]),
                        value, currency(value), points);
#else
                Sprintf(pbuf, "%s(%ld%s，%ldポイントの価値)，",
                        otmp->oartifact ? artifact_name(xname(otmp), &dummy)
                                        : OBJ_NAME(objects[otmp->otyp]),
                        value, currency(value), points);
#endif
                putstr(endwin, 0, pbuf);
            }
        }
        if (Has_contents(otmp))
            artifact_score(otmp->cobj, counting, endwin);
    }
}

/* Be careful not to call panic from here! */
void
done(how)
int how;
{
    if (how == TRICKED) {
        if (killer.name[0]) {
            paniclog("trickery", killer.name);
            killer.name[0] = 0;
        }
        if (wizard) {
/*JP
            You("are a very tricky wizard, it seems.");
*/
            You("とても扱いにくいwizardのようだ．");
            return;
        }
    }

    if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
        killer.format = NO_KILLER_PREFIX;
    /* Avoid killed by "a" burning or "a" starvation */
    if (!killer.name[0] && (how == STARVING || how == BURNING))
        killer.format = KILLED_BY;
    if (!killer.name[0] || how >= PANICKED)
/*JP
        Strcpy(killer.name, deaths[how]);
*/
        Strcpy(killer.name, ends[how]);

    if (how < PANICKED)
        u.umortality++;
    if (Lifesaved && (how <= GENOCIDED)) {
/*JP
        pline("But wait...");
*/
        pline("ちょっとまった．．．");
        makeknown(AMULET_OF_LIFE_SAVING);
/*JP
        Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
*/
        Your("魔除けは%s！", !Blind ? "輝きはじめた" : "暖かくなりはじめた");
        if (how == CHOKING)
/*JP
            You("vomit ...");
*/
            You("吐いた．．．");
/*JP
        You_feel("much better!");
*/
        You("気分がよくなった！");
/*JP
        pline_The("medallion crumbles to dust!");
*/
        pline("魔除けはこなごなにくだけた！");
        if (uamul)
            useup(uamul);

        (void) adjattrib(A_CON, -1, TRUE);
        savelife(how);
        if (how == GENOCIDED) {
/*JP
            pline("Unfortunately you are still genocided...");
*/
            pline("残念ながら，あなたは虐殺されたままだ．．．");
        } else {
            killer.name[0] = 0;
            killer.format = 0;
            return;
        }
    }
    if ((wizard || discover) && (how <= GENOCIDED) &&
/*JP
        !paranoid_query(ParanoidDie, "Die?")) {
*/
        !paranoid_query(ParanoidDie, "死んでみる？")) {
/*JP
        pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
*/
        You("死ななかった．");
        savelife(how);
        killer.name[0] = 0;
        killer.format = 0;
        return;
    }
    really_done(how);
}

/* separated from done() in order to specify the __noreturn__ attribute */
STATIC_OVL void
really_done(how)
int how;
{
    boolean taken;
    char pbuf[BUFSZ];
    winid endwin = WIN_ERR;
    boolean bones_ok, have_windows = iflags.window_inited;
    struct obj *corpse = (struct obj *) 0;
    time_t endtime;
    long umoney;
    long tmp;

    /*
     *  The game is now over...
     */
    program_state.gameover = 1;
    /* in case of a subsequent panic(), there's no point trying to save */
    program_state.something_worth_saving = 0;
    /* render vision subsystem inoperative */
    iflags.vision_inited = 0;

    /* might have been killed while using a disposable item, so make sure
       it's gone prior to inventory disclosure and creation of bones data */
    inven_inuse(TRUE);
    /* maybe not on object lists; if an active light source, would cause
       big trouble (`obj_is_local' panic) for savebones() -> savelev() */
    if (thrownobj && thrownobj->where == OBJ_FREE)
        dealloc_obj(thrownobj);
    if (kickedobj && kickedobj->where == OBJ_FREE)
        dealloc_obj(kickedobj);

    /* remember time of death here instead of having bones, rip, and
       topten figure it out separately and possibly getting different
       time or even day if player is slow responding to --More-- */
    urealtime.finish_time = endtime = getnow();
    urealtime.realtime += (long) (endtime - urealtime.start_timing);

    /* Sometimes you die on the first move.  Life's not fair.
     * On those rare occasions you get hosed immediately, go out
     * smiling... :-)  -3.
     */
    if (moves <= 1 && how < PANICKED) /* You die... --More-- */
/*JP
        pline("Do not pass go.  Do not collect 200 %s.", currency(200L));
*/
        pline("注意一秒，怪我一生，死亡一歩．");

    if (have_windows)
        wait_synch(); /* flush screen output */
#ifndef NO_SIGNAL
    (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
#if defined(UNIX) || defined(VMS) || defined(__EMX__)
    (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
    sethanguphandler(done_hangup);
#endif
#endif /* NO_SIGNAL */

    bones_ok = (how < GENOCIDED) && can_make_bones();

    if (bones_ok && launch_in_progress())
        force_launch_placement();

    /* maintain ugrave_arise even for !bones_ok */
    if (how == PANICKED)
        u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
    else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
        u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
    else if (how == STONING)
        u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
    else if (how == TURNED_SLIME)
        u.ugrave_arise = PM_GREEN_SLIME;

    /* if pets will contribute to score, populate mydogs list now
       (bones creation isn't a factor, but pline() messaging is) */
    if (how == ESCAPED || how == ASCENDED)
        keepdogs(TRUE);

    if (how == QUIT) {
        killer.format = NO_KILLER_PREFIX;
        if (u.uhp < 1) {
            how = DIED;
            u.umortality++; /* skipped above when how==QUIT */
/*JP
            Strcpy(killer.name, "quit while already on Charon's boat");
*/
            Strcpy(killer.name, "三途の川の渡し船に乗っている間に抜けた");
        }
    }
    if (how == ESCAPED || how == PANICKED)
        killer.format = NO_KILLER_PREFIX;

    if (how != PANICKED) {
        /* these affect score and/or bones, but avoid them during panic */
        taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
        paygd();
        clearpriests();
    } else
        taken = FALSE; /* lint; assert( !bones_ok ); */

    clearlocks();

    if (have_windows)
        display_nhwindow(WIN_MESSAGE, FALSE);

    if (strcmp(flags.end_disclose, "none") && how != PANICKED)
        disclose(how, taken);

    /* finish_paybill should be called after disclosure but before bones */
    if (bones_ok && taken)
        finish_paybill();

    /* grave creation should be after disclosure so it doesn't have
       this grave in the current level's features for #overview */
    if (bones_ok && u.ugrave_arise == NON_PM
        && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
        int mnum = u.umonnum;

        if (!Upolyd) {
            /* Base corpse on race when not poly'd since original
             * u.umonnum is based on role, and all role monsters
             * are human.
             */
            mnum = (flags.female && urace.femalenum != NON_PM)
                       ? urace.femalenum
                       : urace.malenum;
        }
        corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
/*JP
        Sprintf(pbuf, "%s, ", plname);
*/
        Sprintf(pbuf, "%sの墓，", plname);
        formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how);
        make_grave(u.ux, u.uy, pbuf);
    }
    pbuf[0] = '\0'; /* clear grave text; also lint suppression */

    /* calculate score, before creating bones [container gold] */
    {
        int deepest = deepest_lev_reached(FALSE);

        umoney = money_cnt(invent);
        tmp = u.umoney0;
        umoney += hidden_gold(); /* accumulate gold from containers */
        tmp = umoney - tmp;      /* net gain */

        if (tmp < 0L)
            tmp = 0L;
        if (how < PANICKED)
            tmp -= tmp / 10L;
        tmp += 50L * (long) (deepest - 1);
        if (deepest > 20)
            tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
        nowrap_add(u.urexp, tmp);

        /* ascension gives a score bonus iff offering to original deity */
        if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
            /* retaining original alignment: score *= 2;
               converting, then using helm-of-OA to switch back: *= 1.5 */
            tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
                      ? u.urexp
                      : (u.urexp / 2L);
            nowrap_add(u.urexp, tmp);
        }
    }

    if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
        /* give this feedback even if bones aren't going to be created,
           so that its presence or absence doesn't tip off the player to
           new bones or their lack; it might be a lie if makemon fails */
/*JP
        Your("body rises from the dead as %s...",
*/
        Your("体は%sとして死体から蘇った．．．",
             an(mons[u.ugrave_arise].mname));
        display_nhwindow(WIN_MESSAGE, FALSE);
    }

    if (bones_ok) {
/*JP
        if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
*/
        if (!wizard || paranoid_query(ParanoidBones, "骨をうめる？"))
            savebones(how, endtime, corpse);
        /* corpse may be invalid pointer now so
            ensure that it isn't used again */
        corpse = (struct obj *) 0;
    }

    /* update gold for the rip output, which can't use hidden_gold()
       (containers will be gone by then if bones just got saved...) */
    done_money = umoney;

    /* clean up unneeded windows */
    if (have_windows) {
        wait_synch();
        free_pickinv_cache(); /* extra persistent window if perm_invent */
        if (WIN_INVEN != WIN_ERR)
            destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
        display_nhwindow(WIN_MESSAGE, TRUE);
        destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
#ifndef STATUS_VIA_WINDOWPORT
        destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
#endif
        destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;

        if (!done_stopprint || flags.tombstone)
            endwin = create_nhwindow(NHW_TEXT);

        if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
            outrip(endwin, how, endtime);
    } else
        done_stopprint = 1; /* just avoid any more output */

    if (u.uhave.amulet) {
/*JP
        Strcat(killer.name, " (with the Amulet)");
*/
        Strcat(killer.name, "魔除けを手に");
    } else if (how == ESCAPED) {
        if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
/*JP
            Strcat(killer.name, " (in celestial disgrace)");
*/
            Strcat(killer.name, "天上で恥辱を受け脱出した");
        else if (carrying(FAKE_AMULET_OF_YENDOR))
/*JP
            Strcat(killer.name, " (with a fake Amulet)");
*/
            Strcat(killer.name, "偽物の魔除けを掴まされ脱出した");
        /* don't bother counting to see whether it should be plural */
    }

    if (!done_stopprint) {
#if 0 /*JP*/
        Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
                (how != ASCENDED)
                 ? (const char *) ((flags.female && urole.name.f)
                                      ? urole.name.f
                                      : urole.name.m)
                 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
#else
        Sprintf(pbuf, "%s%sの%s．．．", Goodbye(),
                how != ASCENDED
                ? (const char *) ((flags.female && urole.name.f)
                                      ? urole.name.f
                                      : urole.name.m)
                : (const char *) (flags.female ? "女神" : "神"),
                plname);
#endif
        putstr(endwin, 0, pbuf);
        putstr(endwin, 0, "");
    }

    if (how == ESCAPED || how == ASCENDED) {
        struct monst *mtmp;
        struct obj *otmp;
        register struct val_list *val;
        register int i;

        for (val = valuables; val->list; val++)
            for (i = 0; i < val->size; i++) {
                val->list[i].count = 0L;
            }
        get_valuables(invent);

        /* add points for collected valuables */
        for (val = valuables; val->list; val++)
            for (i = 0; i < val->size; i++)
                if (val->list[i].count != 0L) {
                    tmp = val->list[i].count
                          * (long) objects[val->list[i].typ].oc_cost;
                    nowrap_add(u.urexp, tmp);
                }

        /* count the points for artifacts */
        artifact_score(invent, TRUE, endwin);

        viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
        mtmp = mydogs;
        if (!done_stopprint)
/*JP
            Strcpy(pbuf, "You");
*/
            Strcpy(pbuf, "あなた");
        if (!Schroedingers_cat) /* check here in case disclosure was off */
            Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
        if (Schroedingers_cat) {
            int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
            mhp = d(m_lev, 8);
            nowrap_add(u.urexp, mhp);
            if (!done_stopprint)
/*JP
                Strcat(eos(pbuf), " and Schroedinger's cat");
*/
                Strcat(eos(pbuf), "とシュレディンガーの猫");
        }
        if (mtmp) {
            while (mtmp) {
                if (!done_stopprint)
/*JP
                    Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
*/
                    Sprintf(eos(pbuf), "と%s", mon_nam(mtmp));
                if (mtmp->mtame)
                    nowrap_add(u.urexp, mtmp->mhp);
                mtmp = mtmp->nmon;
            }
#if 1 /*JP*/
            if (!done_stopprint)
                Strcat(pbuf, "は");
#endif
            if (!done_stopprint)
                putstr(endwin, 0, pbuf);
            pbuf[0] = '\0';
        } else {
            if (!done_stopprint)
/*JP
                Strcat(pbuf, " ");
*/
                Strcat(pbuf, "は");
        }
        if (!done_stopprint) {
#if 0 /*JP*/
            Sprintf(eos(pbuf), "%s with %ld point%s,",
                    how == ASCENDED ? "went to your reward"
                                    : "escaped from the dungeon",
                    u.urexp, plur(u.urexp));
#else
            Sprintf(eos(pbuf), "%ldポイントマークし%s．",
                    u.urexp,
                    how==ASCENDED ? "昇天した" : "迷宮から脱出した");
#endif
            putstr(endwin, 0, pbuf);
        }

        if (!done_stopprint)
            artifact_score(invent, FALSE, endwin); /* list artifacts */

        /* list valuables here */
        for (val = valuables; val->list; val++) {
            sort_valuables(val->list, val->size);
            for (i = 0; i < val->size && !done_stopprint; i++) {
                int typ = val->list[i].typ;
                long count = val->list[i].count;

                if (count == 0L)
                    continue;
                if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
                    otmp = mksobj(typ, FALSE, FALSE);
                    makeknown(otmp->otyp);
                    otmp->known = 1;  /* for fake amulets */
                    otmp->dknown = 1; /* seen it (blindness fix) */
                    if (has_oname(otmp))
                        free_oname(otmp);
                    otmp->quan = count;
#if 0 /*JP*/
                    Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
                            xname(otmp), count * (long) objects[typ].oc_cost,
                            currency(2L));
#else
                    Sprintf(pbuf, "%ld個の%s(%ld%sの価値)，", count,
                            xname(otmp), count * (long) objects[typ].oc_cost,
                            currency(2L));
#endif
                    obfree(otmp, (struct obj *) 0);
                } else {
#if 0 /*JP*/
                    Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
                            count, plur(count));
#else
                    Sprintf(pbuf, "%ld個の価値のない色つきガラス，",
                            count);
#endif
                }
                putstr(endwin, 0, pbuf);
            }
        }

    } else if (!done_stopprint) {
        /* did not escape or ascend */
        if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
            /* level teleported out of the dungeon; `how' is DIED,
               due to falling or to "arriving at heaven prematurely" */
#if 0 /*JP*/
            Sprintf(pbuf, "You %s beyond the confines of the dungeon",
                    (u.uz.dlevel < 0) ? "passed away" : ends[how]);
#else
            Sprintf(pbuf, "迷宮の領域を越え%s．",
                    (u.uz.dlevel < 0) ? "消えさった" : ends[how]);
#endif
        } else {
            /* more conventional demise */
            const char *where = dungeons[u.uz.dnum].dname;

            if (Is_astralevel(&u.uz))
/*JP
                where = "The Astral Plane";
*/
                where = "精霊界にて";
/*JP
            Sprintf(pbuf, "You %s in %s", ends[how], where);
*/
            Sprintf(pbuf, "あなたは%s", where);
            if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
#if 0 /*JP*/
                Sprintf(eos(pbuf), " on dungeon level %d",
                        In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
#else
                Sprintf(eos(pbuf), "の地下%d階で",
                        In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
#endif
        }

/*JP
        Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
*/
        Sprintf(eos(pbuf), " %ldポイントをマークし，", u.urexp);
        putstr(endwin, 0, pbuf);
    }

    if (!done_stopprint) {
#if 0 /*JP*/
        Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
                plur(umoney), moves, plur(moves));
#else
        Sprintf(pbuf, "%ld枚の金貨を持って，%ld歩動いた．",
                umoney, moves);
#endif
        putstr(endwin, 0, pbuf);
    }
    if (!done_stopprint) {
#if 0 /*JP*/
        Sprintf(pbuf,
            "You were level %d with a maximum of %d hit point%s when you %s.",
                u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
#else
        Sprintf(pbuf,
                "%sとき，あなたはレベル%uで，最大体力は%dであった．",
                ends[how],u.ulevel, u.uhpmax);
#endif
        putstr(endwin, 0, pbuf);
        putstr(endwin, 0, "");
    }
    if (!done_stopprint)
        display_nhwindow(endwin, TRUE);
    if (endwin != WIN_ERR)
        destroy_nhwindow(endwin);

    /* "So when I die, the first thing I will see in Heaven is a
     * score list?" */
    if (have_windows && !iflags.toptenwin)
        exit_nhwindows((char *) 0), have_windows = FALSE;
    topten(how, endtime);
    if (have_windows)
        exit_nhwindows((char *) 0);

    if (done_stopprint) {
        raw_print("");
        raw_print("");
    }
    terminate(EXIT_SUCCESS);
}

void
container_contents(list, identified, all_containers, reportempty)
struct obj *list;
boolean identified, all_containers, reportempty;
{
    register struct obj *box, *obj;
    struct obj **oarray;
    int i, n;
    char *invlet;
    char buf[BUFSZ];
    boolean cat, deadcat;

    for (box = list; box; box = box->nobj) {
        if (Is_container(box) || box->otyp == STATUE) {
            box->cknown = 1; /* we're looking at the contents now */
            if (identified)
                box->lknown = 1;
            cat = deadcat = FALSE;
            if (SchroedingersBox(box) && !Schroedingers_cat) {
                /* Schroedinger's Cat? */
                cat = odds_and_ends(box, CAT_CHECK);
                if (cat)
                    Schroedingers_cat = TRUE;
                else
                    deadcat = TRUE;
                box->spe = 0;
            }
            if (box->otyp == BAG_OF_TRICKS) {
                continue; /* wrong type of container */
            } else if (box->cobj) {
                winid tmpwin = create_nhwindow(NHW_MENU);

                /* count the number of items */
                for (n = 0, obj = box->cobj; obj; obj = obj->nobj)
                    n++;
                /* Make a temporary array to store the objects sorted */
                oarray = objarr_init(n);

                /* Add objects to the array */
                i = 0;
                invlet = flags.inv_order;
            nextclass:
                for (obj = box->cobj; obj; obj = obj->nobj) {
                    if (!flags.sortpack || obj->oclass == *invlet) {
                        objarr_set(
                            obj, i++, oarray,
                            (flags.sortloot == 'f' || flags.sortloot == 'l'));
                    }
                } /* for loop */
                if (flags.sortpack) {
                    if (*++invlet)
                        goto nextclass;
                }

/*JP
                Sprintf(buf, "Contents of %s:", the(xname(box)));
*/
                Sprintf(buf, "%sの中身：", the(xname(box)));
                putstr(tmpwin, 0, buf);
                putstr(tmpwin, 0, "");
                for (i = 0; i < n; i++) {
                    obj = oarray[i];
                    if (identified) {
                        makeknown(obj->otyp);
                        obj->known = obj->bknown = obj->dknown = obj->rknown =
                            1;
                        if (Is_container(obj) || obj->otyp == STATUE)
                            obj->cknown = obj->lknown = 1;
                    }
                    putstr(tmpwin, 0, doname(obj));
                }
                free(oarray);
                if (cat)
/*JP
                    putstr(tmpwin, 0, "Schroedinger's cat");
*/
                    putstr(tmpwin, 0, "シュレディンガーの猫");
                else if (deadcat)
/*JP
                    putstr(tmpwin, 0, "Schroedinger's dead cat");
*/
                    putstr(tmpwin, 0, "シュレディンガーの猫の死体");
                display_nhwindow(tmpwin, TRUE);
                destroy_nhwindow(tmpwin);
                if (all_containers)
                    container_contents(box->cobj, identified, TRUE,
                                       reportempty);
            } else if (cat || deadcat) {
#if 0 /*JP*/
                pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
                      deadcat ? "dead " : "");
#else
                pline("%sにはシュレディンガーの猫%sが入っていた！", xname(box),
                      deadcat ? "の死体" : "");
#endif
                display_nhwindow(WIN_MESSAGE, FALSE);
            } else if (reportempty) {
/*JP
                pline("%s is empty.", upstart(thesimpleoname(box)));
*/
                pline("%sは空っぽだ．", xname(box));
                display_nhwindow(WIN_MESSAGE, FALSE);
            }
        }
        if (!all_containers)
            break;
    }
}

/* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
void
terminate(status)
int status;
{
    program_state.in_moveloop = 0; /* won't be returning to normal play */
#ifdef MAC
    getreturn("to exit");
#endif
    /* don't bother to try to release memory if we're in panic mode, to
       avoid trouble in case that happens to be due to memory problems */
    if (!program_state.panicking) {
        freedynamicdata();
        dlb_cleanup();
    }

#ifdef VMS
    /*
     *  This is liable to draw a warning if compiled with gcc, but it's
     *  more important to flag panic() -> really_done() -> terminate()
     *  as __noreturn__ then to avoid the warning.
     */
    /* don't call exit() if already executing within an exit handler;
       that would cancel any other pending user-mode handlers */
    if (program_state.exiting)
        return;
#endif
    program_state.exiting = 1;
#if 1 /*JP*/
    jputchar('\0'); /* reset terminal */
#endif
    nethack_exit(status);
}

/* #vanquished command */
int
dovanquished()
{
    list_vanquished('a', FALSE);
    return 0;
}

STATIC_OVL void
list_vanquished(defquery, ask)
char defquery;
boolean ask;
{
    register int i, lev;
    int ntypes = 0, max_lev = 0, nkilled;
    long total_killed = 0L;
    char c;
    winid klwin;
    char buf[BUFSZ];

    /* get totals first */
    for (i = LOW_PM; i < NUMMONS; i++) {
        if (mvitals[i].died)
            ntypes++;
        total_killed += (long) mvitals[i].died;
        if (mons[i].mlevel > max_lev)
            max_lev = mons[i].mlevel;
    }

    /* vanquished creatures list;
     * includes all dead monsters, not just those killed by the player
     */
    if (ntypes != 0) {
#if 0 /*JP*/
        c = ask ? yn_function(
                            "Do you want an account of creatures vanquished?",
                              ynqchars, defquery)
                : defquery;
#else
        c = ask ? yn_function(
                            "倒した敵の一覧を見ますか？",
                              ynqchars, defquery)
                : defquery;
#endif
        if (c == 'q')
            done_stopprint++;
        if (c == 'y' || c == 'a') {
            klwin = create_nhwindow(NHW_MENU);
/*JP
            putstr(klwin, 0, "Vanquished creatures:");
*/
            putstr(klwin, 0, "倒した敵：");
            putstr(klwin, 0, "");

            /* countdown by monster "toughness" */
            for (lev = max_lev; lev >= 0; lev--)
                for (i = LOW_PM; i < NUMMONS; i++)
                    if (mons[i].mlevel == lev
                        && (nkilled = mvitals[i].died) > 0) {
                        if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) {
#if 0 /*JP*/
                            Sprintf(buf, "%s%s",
                                    !type_is_pname(&mons[i]) ? "The " : "",
                                    mons[i].mname);
#else
                            Sprintf(buf, "%s", mons[i].mname);
#endif
                            if (nkilled > 1) {
#if 0 /*JP*/
                                switch (nkilled) {
                                case 2:
                                    Sprintf(eos(buf), " (twice)");
                                    break;
                                case 3:
                                    Sprintf(eos(buf), " (thrice)");
                                    break;
                                default:
                                    Sprintf(eos(buf), " (%d times)", nkilled);
                                    break;
                                }
#else
                                Sprintf(eos(buf)," (%d回)", nkilled);
#endif
                            }
                        } else {
                            /* trolls or undead might have come back,
                               but we don't keep track of that */
                            if (nkilled == 1)
                                Strcpy(buf, an(mons[i].mname));
                            else
#if 0 /*JP*/
                                Sprintf(buf, "%d %s", nkilled,
                                        makeplural(mons[i].mname));
#else
                                Sprintf(buf, "%d体の%s", nkilled,
                                        mons[i].mname);
#endif
                        }
                        putstr(klwin, 0, buf);
                    }
            /*
             * if (Hallucination)
             *     putstr(klwin, 0, "and a partridge in a pear tree");
             */
            if (ntypes > 1) {
                putstr(klwin, 0, "");
/*JP
                Sprintf(buf, "%ld creatures vanquished.", total_killed);
*/
                Sprintf(buf, "%ld匹の敵を倒した．", total_killed);
                putstr(klwin, 0, buf);
            }
            display_nhwindow(klwin, TRUE);
            destroy_nhwindow(klwin);
        }
    } else if (defquery == 'a') {
        /* #dovanquished rather than final disclosure, so pline() is ok */
/*JP
        pline("No monsters have been vanquished.");
*/
        pline("倒した敵はいなかった．");
    }
}

/* number of monster species which have been genocided */
int
num_genocides()
{
    int i, n = 0;

    for (i = LOW_PM; i < NUMMONS; ++i)
        if (mvitals[i].mvflags & G_GENOD)
            ++n;

    return n;
}

int
num_extinct()
{
    int i, n = 0;

    for (i = LOW_PM; i < NUMMONS; ++i)
        if (!(mvitals[i].mvflags & G_GENOD) && (mvitals[i].mvflags & G_GONE)
            && !(mons[i].geno & G_UNIQ))
            ++n;

    return n;
}

STATIC_OVL void
list_genocided(defquery, ask)
char defquery;
boolean ask;
{
    register int i;
    int ngenocided, nextinct;
    char c;
    winid klwin;
    char buf[BUFSZ];

    ngenocided = num_genocides();
    nextinct = num_extinct();

    /* genocided or extinct species list */
    if (ngenocided != 0 || nextinct != 0) {
#if 0 /*JP*/
        Sprintf(buf, "Do you want a list of %sspecies%s%s?",
                (nextinct && !ngenocided) ? "extinct " : "",
                (ngenocided) ? " genocided" : "",
                (nextinct && ngenocided) ? " and extinct" : "");
#else
        Sprintf(buf, "%s%s%sした種の一覧を見ますか？",
                (nextinct && !ngenocided) ? "絶滅" : "",
                (ngenocided) ? "虐殺" : "",
                (nextinct && ngenocided) ? "および絶滅" : "");
#endif
        c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
        if (c == 'q')
            done_stopprint++;
        if (c == 'y') {
            klwin = create_nhwindow(NHW_MENU);
#if 0 /*JP*/
            Sprintf(buf, "%s%s species:",
                    (ngenocided) ? "Genocided" : "Extinct",
                    (nextinct && ngenocided) ? " or extinct" : "");
#else
            Sprintf(buf, "%s%sした種:",
                    (ngenocided) ? "虐殺" : "絶滅",
                    (nextinct && ngenocided) ? "または絶滅" : "");
#endif
            putstr(klwin, 0, buf);
            putstr(klwin, 0, "");

            for (i = LOW_PM; i < NUMMONS; i++)
                if (mvitals[i].mvflags & G_GONE && !(mons[i].geno & G_UNIQ)) {
                    if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST)
#if 0 /*JP*/
                        Sprintf(buf, "%s%s",
                                !type_is_pname(&mons[i]) ? "" : "the ",
                                mons[i].mname);
#else
                        Sprintf(buf, "%s", mons[i].mname);
#endif
                    else
                        Strcpy(buf, makeplural(mons[i].mname));
                    if (!(mvitals[i].mvflags & G_GENOD))
/*JP
                        Strcat(buf, " (extinct)");
*/
                        Strcat(buf, "(絶滅)");
                    putstr(klwin, 0, buf);
                }

            putstr(klwin, 0, "");
            if (ngenocided > 0) {
/*JP
                Sprintf(buf, "%d species genocided.", ngenocided);
*/
                Sprintf(buf, "%d種類の種を虐殺した．", ngenocided);
                putstr(klwin, 0, buf);
            }
            if (nextinct > 0) {
/*JP
                Sprintf(buf, "%d species extinct.", nextinct);
*/
                Sprintf(buf, "%d種類の種を絶滅させた．", nextinct);
                putstr(klwin, 0, buf);
            }

            display_nhwindow(klwin, TRUE);
            destroy_nhwindow(klwin);
        }
    }
}

/* set a delayed killer, ensure non-delayed killer is cleared out */
void
delayed_killer(id, format, killername)
int id;
int format;
const char *killername;
{
    struct kinfo *k = find_delayed_killer(id);

    if (k == (struct kinfo *) 0) {
        /* no match, add a new delayed killer to the list */
        k = (struct kinfo *) alloc(sizeof(struct kinfo));
        k->id = id;
        k->next = killer.next;
        killer.next = k;
    }

    k->format = format;
    Strcpy(k->name, killername ? killername : "");
    killer.name[0] = 0;
}

struct kinfo *
find_delayed_killer(id)
int id;
{
    struct kinfo *k;

    for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
        if (k->id == id)
            break;
    }
    return k;
}

void
dealloc_killer(kptr)
struct kinfo *kptr;
{
    struct kinfo *prev = &killer, *k;

    if (kptr == (struct kinfo *) 0)
        return;
    for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
        if (k == kptr)
            break;
        prev = k;
    }

    if (k == (struct kinfo *) 0) {
        impossible("dealloc_killer not on list");
    } else {
        prev->next = k->next;
        free((genericptr_t) k);
    }
}

void
save_killers(fd, mode)
int fd;
int mode;
{
    struct kinfo *kptr;

    if (perform_bwrite(mode)) {
        for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
            bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
        }
    }
    if (release_data(mode)) {
        while (killer.next) {
            kptr = killer.next->next;
            free((genericptr_t) killer.next);
            killer.next = kptr;
        }
    }
}

void
restore_killers(fd)
int fd;
{
    struct kinfo *kptr;

    for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
        mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
        if (kptr->next) {
            kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
        }
    }
}

static int
wordcount(p)
char *p;
{
    int words = 0;

    while (*p) {
        while (*p && isspace((uchar) *p))
            p++;
        if (*p)
            words++;
        while (*p && !isspace((uchar) *p))
            p++;
    }
    return words;
}

static void
bel_copy1(inp, out)
char **inp, *out;
{
    char *in = *inp;

    out += strlen(out); /* eos() */
    while (*in && isspace((uchar) *in))
        in++;
    while (*in && !isspace((uchar) *in))
        *out++ = *in++;
    *out = '\0';
    *inp = in;
}

char *
build_english_list(in)
char *in;
{
    char *out, *p = in;
    int len = (int) strlen(p), words = wordcount(p);

    /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
    if (words > 1)
        len += 3 + (words - 1);
    out = (char *) alloc(len + 1);
    *out = '\0'; /* bel_copy1() appends */

    switch (words) {
    case 0:
        impossible("no words in list");
        break;
    case 1:
        /* "single" */
        bel_copy1(&p, out);
        break;
    default:
        if (words == 2) {
            /* "first or second" */
            bel_copy1(&p, out);
            Strcat(out, " ");
        } else {
            /* "first, second, or third */
            do {
                bel_copy1(&p, out);
                Strcat(out, ", ");
            } while (--words > 1);
        }
        Strcat(out, "or ");
        bel_copy1(&p, out);
        break;
    }
    return out;
}

/*end.c*/
