1 /* NetHack 3.6 end.c $NHDT-Date: 1461919723 2016/04/29 08:48:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #define NEED_VARARGS /* comment line for pre-compiled headers */
16 /* add b to long a, convert wraparound to max value */
17 #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
19 /* these probably ought to be generated by makedefs, like LAST_GEM */
20 #define FIRST_GEM DILITHIUM_CRYSTAL
21 #define FIRST_AMULET AMULET_OF_ESP
22 #define LAST_AMULET AMULET_OF_YENDOR
24 struct valuable_data
{
29 static struct valuable_data
30 gems
[LAST_GEM
+ 1 - FIRST_GEM
+ 1], /* 1 extra for glass */
31 amulets
[LAST_AMULET
+ 1 - FIRST_AMULET
];
33 static struct val_list
{
34 struct valuable_data
*list
;
36 } valuables
[] = { { gems
, sizeof gems
/ sizeof *gems
},
37 { amulets
, sizeof amulets
/ sizeof *amulets
},
41 STATIC_PTR
void FDECL(done_intr
, (int));
42 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
43 static void FDECL(done_hangup
, (int));
46 STATIC_DCL
void FDECL(disclose
, (int, BOOLEAN_P
));
47 STATIC_DCL
void FDECL(get_valuables
, (struct obj
*));
48 STATIC_DCL
void FDECL(sort_valuables
, (struct valuable_data
*, int));
49 STATIC_DCL
void FDECL(artifact_score
, (struct obj
*, BOOLEAN_P
, winid
));
50 STATIC_DCL
void FDECL(really_done
, (int)) NORETURN
;
51 STATIC_DCL boolean
FDECL(odds_and_ends
, (struct obj
*, int));
52 STATIC_DCL
void FDECL(savelife
, (int));
53 STATIC_PTR
int FDECL(CFDECLSPEC vanqsort_cmp
, (const genericptr
,
55 STATIC_DCL
int NDECL(set_vanq_order
);
56 STATIC_DCL
void FDECL(list_vanquished
, (CHAR_P
, BOOLEAN_P
));
57 STATIC_DCL
void FDECL(list_genocided
, (CHAR_P
, BOOLEAN_P
));
58 STATIC_DCL boolean
FDECL(should_query_disclose_option
, (int, char *));
59 STATIC_DCL
int NDECL(num_extinct
);
61 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
62 extern void FDECL(nethack_exit
, (int));
64 #define nethack_exit exit
67 #define done_stopprint program_state.stopprint
70 #define NH_abort NH_abort_
74 #define NH_abort_() Abort(0)
77 #define NH_abort_() (void) abort()
80 #define NH_abort_() win32_abort()
82 #define NH_abort_() abort()
89 #ifdef PANICTRACE_LIBC
93 /* What do we try and in what order? Tradeoffs:
94 * libc: +no external programs required
95 * -requires newish libc/glibc
97 * gdb: +gives more detailed information
98 * +works on more OS versions
99 * -requires -g, which may preclude -O on some compilers
102 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
103 #ifdef PANICTRACE_LIBC
104 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
106 #define SYSOPT_PANICTRACE_LIBC 0
109 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
110 #ifdef PANICTRACE_LIBC
111 #define SYSOPT_PANICTRACE_LIBC 1
113 #define SYSOPT_PANICTRACE_LIBC 0
117 static void NDECL(NH_abort
);
119 static void FDECL(panictrace_handler
, (int));
121 static boolean
NDECL(NH_panictrace_libc
);
122 static boolean
NDECL(NH_panictrace_gdb
);
126 void panictrace_handler(
127 sig_unused
) /* called as signal() handler, so sent at least one arg */
128 int sig_unused UNUSED
;
130 #define SIG_MSG "\nSignal received.\n"
131 (void) write(2, SIG_MSG
, sizeof(SIG_MSG
) - 1);
136 panictrace_setsignals(set
)
139 #define SETSIGNAL(sig) \
140 (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
160 SETSIGNAL(SIGSTKFLT
);
170 #endif /* NO_SIGNAL */
175 int gdb_prio
= SYSOPT_PANICTRACE_GDB
;
176 int libc_prio
= SYSOPT_PANICTRACE_LIBC
;
177 static boolean aborting
= FALSE
;
184 if (gdb_prio
== libc_prio
&& gdb_prio
> 0)
187 if (gdb_prio
> libc_prio
) {
188 (void) (NH_panictrace_gdb() || (libc_prio
&& NH_panictrace_libc()));
190 (void) (NH_panictrace_libc() || (gdb_prio
&& NH_panictrace_gdb()));
194 /* overload otherwise unused priority for debug mode: 1 = show
195 traceback and exit; 2 = show traceback and stay in debugger */
196 /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
197 vms_traceback(gdb_prio
);
198 (void) libc_prio
; /* half-hearted attempt at lint suppression */
203 panictrace_setsignals(FALSE
);
211 #ifdef PANICTRACE_LIBC
216 raw_print("Generating more information you may report:\n");
217 count
= backtrace(bt
, SIZE(bt
));
218 info
= backtrace_symbols(bt
, count
);
219 for (x
= 0; x
< count
; x
++) {
220 raw_printf("[%lu] %s", (unsigned long) x
, info
[x
]);
222 /* free(info); -- Don't risk it. */
226 #endif /* !PANICTRACE_LIBC */
230 * fooPATH file system path for foo
231 * fooVAR (possibly const) variable containing fooPATH
233 #ifdef PANICTRACE_GDB
235 #define GDBVAR sysopt.gdbpath
236 #define GREPVAR sysopt.greppath
238 #define GDBVAR GDBPATH
239 #define GREPVAR GREPPATH
241 #endif /* PANICTRACE_GDB */
246 #ifdef PANICTRACE_GDB
247 /* A (more) generic method to get a stack trace - invoke
249 char *gdbpath
= GDBVAR
;
250 char *greppath
= GREPVAR
;
254 if (gdbpath
== NULL
|| gdbpath
[0] == 0)
256 if (greppath
== NULL
|| greppath
[0] == 0)
259 sprintf(buf
, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath
, ARGV0
, getpid(),
261 gdb
= popen(buf
, "w");
263 raw_print("Generating more information you may report:\n");
264 fprintf(gdb
, "bt\nquit\ny");
274 #endif /* !PANICTRACE_GDB */
276 #endif /* PANICTRACE */
279 * The order of these needs to match the macros in hack.h.
281 static NEARDATA
const char *deaths
[] = {
282 /* the array of death */
283 "died", "choked", "poisoned", "starvation", "drowning", "burning",
284 "dissolving under the heat and pressure", "crushed", "turned to stone",
285 "turned into slime", "genocided", "panic", "trickery", "quit",
286 "escaped", "ascended"
289 static NEARDATA
const char *ends
[] = {
291 "died", "choked", "were poisoned",
292 "starved", "drowned", "burned",
293 "dissolved in the lava",
294 "were crushed", "turned to stone",
295 "turned into slime", "were genocided",
296 "panicked", "were tricked", "quit",
297 "escaped", "ascended"
300 static boolean Schroedingers_cat
= FALSE
;
304 done1(sig_unused
) /* called as signal() handler, so sent at least one arg */
305 int sig_unused UNUSED
;
308 (void) signal(SIGINT
, SIG_IGN
);
312 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
314 clear_nhwindow(WIN_MESSAGE
);
324 /* "#quit" command or keyboard interrupt */
328 if (!paranoid_query(ParanoidQuit
, "Really quit?")) {
330 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
332 clear_nhwindow(WIN_MESSAGE
);
338 u
.uinvulnerable
= FALSE
; /* avoid ctrl-C bug -dlc */
343 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
347 extern int debuggable
; /* sys/vms/vmsmisc.c, vmsunix.c */
349 c
= !debuggable
? 'n' : ynq("Enter debugger?");
352 c
= ynq("Create SnapShot?");
354 c
= ynq("Dump core?");
359 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
361 exit_nhwindows((char *) 0);
376 done_intr(sig_unused
) /* called as signal() handler, so sent at least 1 arg */
377 int sig_unused UNUSED
;
380 (void) signal(SIGINT
, SIG_IGN
);
381 #if defined(UNIX) || defined(VMS)
382 (void) signal(SIGQUIT
, SIG_IGN
);
387 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
388 /* signal() handler */
393 program_state
.done_hup
++;
394 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
399 #endif /* NO_SIGNAL */
402 done_in_by(mtmp
, how
)
407 struct permonst
*mptr
= mtmp
->data
,
408 *champtr
= ((mtmp
->cham
>= LOW_PM
)
411 boolean distorted
= (boolean
) (Hallucination
&& canspotmon(mtmp
)),
412 mimicker
= (mtmp
->m_ap_type
== M_AP_MONSTER
),
413 imitator
= (mptr
!= champtr
|| mimicker
);
415 You((how
== STONING
) ? "turn to stone..." : "die...");
416 mark_synch(); /* flush buffered screen output */
418 killer
.format
= KILLED_BY_AN
;
419 /* "killed by the high priest of Crom" is okay,
420 "killed by the high priest" alone isn't */
421 if ((mptr
->geno
& G_UNIQ
) != 0 && !(imitator
&& !mimicker
)
422 && !(mptr
== &mons
[PM_HIGH_PRIEST
] && !mtmp
->ispriest
)) {
423 if (!type_is_pname(mptr
))
425 killer
.format
= KILLED_BY
;
427 /* _the_ <invisible> <distorted> ghost of Dudley */
428 if (mptr
== &mons
[PM_GHOST
] && has_mname(mtmp
)) {
430 killer
.format
= KILLED_BY
;
433 Strcat(buf
, "invisible ");
435 Strcat(buf
, "hallucinogen-distorted ");
439 const char *realnm
= champtr
->mname
, *fakenm
= mptr
->mname
;
440 boolean alt
= is_vampshifter(mtmp
);
443 /* realnm is already correct because champtr==mptr;
444 set up fake mptr for type_is_pname/the_unique_pm */
445 mptr
= &mons
[mtmp
->mappearance
];
446 fakenm
= mptr
->mname
;
447 } else if (alt
&& strstri(realnm
, "vampire")
448 && !strcmp(fakenm
, "vampire bat")) {
449 /* special case: use "vampire in bat form" in preference
450 to redundant looking "vampire in vampire bat form" */
453 /* for the alternate format, always suppress any article;
454 pname and the_unique should also have s_suffix() applied,
455 but vampires don't take on any shapes which warrant that */
456 if (alt
|| type_is_pname(mptr
)) /* no article */
457 Strcpy(shape
, fakenm
);
458 else if (the_unique_pm(mptr
)) /* "the"; don't use the() here */
459 Sprintf(shape
, "the %s", fakenm
);
461 Strcpy(shape
, an(fakenm
));
462 /* omit "called" to avoid excessive verbosity */
464 alt
? "%s in %s form"
465 : mimicker
? "%s disguised as %s"
468 mptr
= mtmp
->data
; /* reset for mimicker case */
469 } else if (mptr
== &mons
[PM_GHOST
]) {
470 Strcat(buf
, "ghost");
472 Sprintf(eos(buf
), " of %s", MNAME(mtmp
));
473 } else if (mtmp
->isshk
) {
474 const char *shknm
= shkname(mtmp
),
475 *honorific
= shkname_is_pname(mtmp
) ? ""
476 : mtmp
->female
? "Ms. " : "Mr. ";
478 Sprintf(eos(buf
), "%s%s, the shopkeeper", honorific
, shknm
);
479 killer
.format
= KILLED_BY
;
480 } else if (mtmp
->ispriest
|| mtmp
->isminion
) {
481 /* m_monnam() suppresses "the" prefix plus "invisible", and
482 it overrides the effect of Hallucination on priestname() */
483 Strcat(buf
, m_monnam(mtmp
));
485 Strcat(buf
, mptr
->mname
);
487 Sprintf(eos(buf
), " called %s", MNAME(mtmp
));
490 Strcpy(killer
.name
, buf
);
491 if (mptr
->mlet
== S_WRAITH
)
492 u
.ugrave_arise
= PM_WRAITH
;
493 else if (mptr
->mlet
== S_MUMMY
&& urace
.mummynum
!= NON_PM
)
494 u
.ugrave_arise
= urace
.mummynum
;
495 else if (mptr
->mlet
== S_VAMPIRE
&& Race_if(PM_HUMAN
))
496 u
.ugrave_arise
= PM_VAMPIRE
;
497 else if (mptr
== &mons
[PM_GHOUL
])
498 u
.ugrave_arise
= PM_GHOUL
;
499 /* this could happen if a high-end vampire kills the hero
500 when ordinary vampires are genocided; ditto for wraiths */
501 if (u
.ugrave_arise
>= LOW_PM
502 && (mvitals
[u
.ugrave_arise
].mvflags
& G_GENOD
))
503 u
.ugrave_arise
= NON_PM
;
509 /* some special cases for overriding while-helpless reason */
510 static const struct {
512 const char *exclude
, *include
;
514 /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
515 prevented any last-second recovery, but it was not the cause of
516 "petrified by <foo>" */
517 { STONING
, 1, "getting stoned", (char *) 0 },
518 /* "died of starvation, while fainted from lack of food" is accurate
519 but sounds a fairly silly (and doesn't actually appear unless you
520 splice together death and while-helpless from xlogfile) */
521 { STARVING
, 0, "fainted from lack of food", "fainted" },
524 /* clear away while-helpless when the cause of death caused that
525 helplessness (ie, "petrified by <foo> while getting stoned") */
533 for (i
= 0; i
< SIZE(death_fixups
); ++i
)
534 if (death_fixups
[i
].why
== how
535 && !strcmp(death_fixups
[i
].exclude
, multi_reason
)) {
536 if (death_fixups
[i
].include
) /* substitute alternate reason */
537 multi_reason
= death_fixups
[i
].include
;
538 else /* remove the helplessness reason */
539 multi_reason
= (char *) 0;
540 if (death_fixups
[i
].unmulti
) /* possibly hide helplessness */
547 #if defined(WIN32) && !defined(SYSCF)
548 #define NOTIFY_NETHACK_BUGS
553 VA_DECL(const char *, str
)
556 VA_INIT(str
, char *);
558 if (program_state
.panicking
++)
559 NH_abort(); /* avoid loops - this should never happen*/
561 if (iflags
.window_inited
) {
562 raw_print("\r\nOops...");
563 wait_synch(); /* make sure all pending output gets flushed */
564 exit_nhwindows((char *) 0);
565 iflags
.window_inited
= 0; /* they're gone; force raw_print()ing */
568 raw_print(program_state
.gameover
569 ? "Postgame wrapup disrupted."
570 : !program_state
.something_worth_saving
571 ? "Program initialization has failed."
572 : "Suddenly, the dungeon collapses.");
574 #if defined(NOTIFY_NETHACK_BUGS)
576 raw_printf("Report the following error to \"%s\" or at \"%s\".",
577 DEVTEAM_EMAIL
, DEVTEAM_URL
);
578 else if (program_state
.something_worth_saving
)
579 raw_print("\nError save file being written.\n");
582 const char *maybe_rebuild
= !program_state
.something_worth_saving
584 : "\nand it may be possible to rebuild.";
587 raw_printf("To report this error, %s%s", sysopt
.support
,
589 else if (sysopt
.fmtd_wizard_list
) /* formatted SYSCF WIZARDS */
590 raw_printf("To report this error, contact %s%s",
591 sysopt
.fmtd_wizard_list
, maybe_rebuild
);
593 raw_printf("Report error to \"%s\"%s", WIZARD_NAME
,
597 /* XXX can we move this above the prints? Then we'd be able to
598 * suppress "it may be possible to rebuild" based on dosave0()
599 * or say it's NOT possible to rebuild. */
600 if (program_state
.something_worth_saving
) {
601 set_error_savefile();
603 /* os/win port specific recover instructions */
605 raw_printf("%s", sysopt
.recover
);
612 Vsprintf(buf
, str
, VA_ARGS
);
614 paniclog("panic", buf
);
617 interject(INTERJECT_PANIC
);
619 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
621 NH_abort(); /* generate core dump */
624 really_done(PANICKED
);
628 should_query_disclose_option(category
, defquery
)
636 if ((dop
= index(disclosure_options
, category
)) != 0) {
637 idx
= (int) (dop
- disclosure_options
);
638 if (idx
< 0 || idx
>= NUM_DISCLOSURE_OPTIONS
) {
640 "should_query_disclose_option: bad disclosure index %d %c",
642 *defquery
= DISCLOSE_PROMPT_DEFAULT_YES
;
645 disclose
= flags
.end_disclose
[idx
];
646 if (disclose
== DISCLOSE_YES_WITHOUT_PROMPT
) {
649 } else if (disclose
== DISCLOSE_SPECIAL_WITHOUT_PROMPT
) {
652 } else if (disclose
== DISCLOSE_NO_WITHOUT_PROMPT
) {
655 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_YES
) {
658 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_SPECIAL
) {
666 impossible("should_query_disclose_option: bad category %c", category
);
675 char c
= '\0', defquery
;
679 if (invent
&& !done_stopprint
) {
681 Sprintf(qbuf
, "Do you want to see what you had when you %s?",
682 (how
== QUIT
) ? "quit" : "died");
684 Strcpy(qbuf
, "Do you want your possessions identified?");
686 ask
= should_query_disclose_option('i', &defquery
);
687 c
= ask
? yn_function(qbuf
, ynqchars
, defquery
) : defquery
;
691 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
692 makeknown(obj
->otyp
);
693 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
694 if (Is_container(obj
) || obj
->otyp
== STATUE
)
695 obj
->cknown
= obj
->lknown
= 1;
697 (void) display_inventory((char *) 0, TRUE
);
698 container_contents(invent
, TRUE
, TRUE
, FALSE
);
704 if (!done_stopprint
) {
705 ask
= should_query_disclose_option('a', &defquery
);
706 c
= ask
? yn_function("Do you want to see your attributes?", ynqchars
,
710 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
711 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
717 if (!done_stopprint
) {
718 ask
= should_query_disclose_option('v', &defquery
);
719 list_vanquished(defquery
, ask
);
722 if (!done_stopprint
) {
723 ask
= should_query_disclose_option('g', &defquery
);
724 list_genocided(defquery
, ask
);
727 if (!done_stopprint
) {
728 ask
= should_query_disclose_option('c', &defquery
);
729 c
= ask
? yn_function("Do you want to see your conduct?", ynqchars
,
733 show_conduct((how
>= PANICKED
) ? 1 : 2);
738 if (!done_stopprint
) {
739 ask
= should_query_disclose_option('o', &defquery
);
740 c
= ask
? yn_function("Do you want to see the dungeon overview?",
744 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
750 /* try to get the player back in a viable state after being killed */
755 int uhpmin
= max(2 * u
.ulevel
, 10);
757 if (u
.uhpmax
< uhpmin
)
761 if (u
.uhunger
< 500) {
765 /* cure impending doom of sickness hero won't have time to fix */
766 if ((Sick
& TIMEOUT
) == 1L) {
768 set_itimeout(&Sick
, 0L);
772 nomovemsg
= "You survived that attempt on your life.";
778 if (u
.utrap
&& u
.utraptype
== TT_LAVA
)
781 u
.ugrave_arise
= NON_PM
;
787 * Get valuables from the given list. Revised code: the list always remains
792 struct obj
*list
; /* inventory or container contents */
794 register struct obj
*obj
;
797 /* find amulets and gems, ignoring all artifacts */
798 for (obj
= list
; obj
; obj
= obj
->nobj
)
799 if (Has_contents(obj
)) {
800 get_valuables(obj
->cobj
);
801 } else if (obj
->oartifact
) {
803 } else if (obj
->oclass
== AMULET_CLASS
) {
804 i
= obj
->otyp
- FIRST_AMULET
;
805 if (!amulets
[i
].count
) {
806 amulets
[i
].count
= obj
->quan
;
807 amulets
[i
].typ
= obj
->otyp
;
809 amulets
[i
].count
+= obj
->quan
; /* always adds one */
810 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
811 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
812 if (!gems
[i
].count
) {
813 gems
[i
].count
= obj
->quan
;
814 gems
[i
].typ
= obj
->otyp
;
816 gems
[i
].count
+= obj
->quan
;
822 * Sort collected valuables, most frequent to least. We could just
823 * as easily use qsort, but we don't care about efficiency here.
826 sort_valuables(list
, size
)
827 struct valuable_data list
[];
828 int size
; /* max value is less than 20 */
831 struct valuable_data ltmp
;
833 /* move greater quantities to the front of the list */
834 for (i
= 1; i
< size
; i
++) {
835 if (list
[i
].count
== 0)
836 continue; /* empty slot */
837 ltmp
= list
[i
]; /* structure copy */
838 for (j
= i
; j
> 0; --j
)
839 if (list
[j
- 1].count
>= ltmp
.count
)
842 list
[j
] = list
[j
- 1];
852 odds_and_ends(list
, what
)
857 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
859 case CAT_CHECK
: /* Schroedinger's Cat */
860 /* Ascending is deterministic */
861 if (SchroedingersBox(otmp
))
865 if (Has_contents(otmp
))
866 return odds_and_ends(otmp
->cobj
, what
);
871 /* called twice; first to calculate total, then to list relevant items */
873 artifact_score(list
, counting
, endwin
)
875 boolean counting
; /* true => add up points; false => display them */
881 short dummy
; /* object type returned by artifact_name() */
883 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
884 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
885 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
886 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
887 value
= arti_cost(otmp
); /* zorkmid value */
888 points
= value
* 5 / 2; /* score value */
890 nowrap_add(u
.urexp
, points
);
892 makeknown(otmp
->otyp
);
893 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
894 /* assumes artifacts don't have quan > 1 */
895 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
896 the_unique_obj(otmp
) ? "The " : "",
897 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
898 : OBJ_NAME(objects
[otmp
->otyp
]),
899 value
, currency(value
), points
);
900 putstr(endwin
, 0, pbuf
);
903 if (Has_contents(otmp
))
904 artifact_score(otmp
->cobj
, counting
, endwin
);
908 /* Be careful not to call panic from here! */
913 if (how
== TRICKED
) {
914 if (killer
.name
[0]) {
915 paniclog("trickery", killer
.name
);
919 You("are a very tricky wizard, it seems.");
924 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
925 killer
.format
= NO_KILLER_PREFIX
;
926 /* Avoid killed by "a" burning or "a" starvation */
927 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
928 killer
.format
= KILLED_BY
;
929 if (!killer
.name
[0] || how
>= PANICKED
)
930 Strcpy(killer
.name
, deaths
[how
]);
934 if (Lifesaved
&& (how
<= GENOCIDED
)) {
935 pline("But wait...");
936 makeknown(AMULET_OF_LIFE_SAVING
);
937 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
940 You_feel("much better!");
941 pline_The("medallion crumbles to dust!");
945 (void) adjattrib(A_CON
, -1, TRUE
);
947 if (how
== GENOCIDED
) {
948 pline("Unfortunately you are still genocided...");
955 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
956 && !paranoid_query(ParanoidDie
, "Die?")) {
957 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
966 /* separated from done() in order to specify the __noreturn__ attribute */
973 winid endwin
= WIN_ERR
;
974 boolean bones_ok
, have_windows
= iflags
.window_inited
;
975 struct obj
*corpse
= (struct obj
*) 0;
981 * The game is now over...
983 program_state
.gameover
= 1;
984 /* in case of a subsequent panic(), there's no point trying to save */
985 program_state
.something_worth_saving
= 0;
986 /* render vision subsystem inoperative */
987 iflags
.vision_inited
= 0;
989 /* might have been killed while using a disposable item, so make sure
990 it's gone prior to inventory disclosure and creation of bones data */
992 /* maybe not on object lists; if an active light source, would cause
993 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
994 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
995 dealloc_obj(thrownobj
);
996 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
997 dealloc_obj(kickedobj
);
999 /* remember time of death here instead of having bones, rip, and
1000 topten figure it out separately and possibly getting different
1001 time or even day if player is slow responding to --More-- */
1002 urealtime
.finish_time
= endtime
= getnow();
1003 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1005 /* Sometimes you die on the first move. Life's not fair.
1006 * On those rare occasions you get hosed immediately, go out
1007 * smiling... :-) -3.
1009 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1010 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1013 wait_synch(); /* flush screen output */
1015 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1016 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1017 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1018 sethanguphandler(done_hangup
);
1020 #endif /* NO_SIGNAL */
1022 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1024 if (bones_ok
&& launch_in_progress())
1025 force_launch_placement();
1027 /* maintain ugrave_arise even for !bones_ok */
1028 if (how
== PANICKED
)
1029 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1030 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1031 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1032 else if (how
== STONING
)
1033 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1034 else if (how
== TURNED_SLIME
)
1035 u
.ugrave_arise
= PM_GREEN_SLIME
;
1037 /* if pets will contribute to score, populate mydogs list now
1038 (bones creation isn't a factor, but pline() messaging is) */
1039 if (how
== ESCAPED
|| how
== ASCENDED
)
1043 killer
.format
= NO_KILLER_PREFIX
;
1046 u
.umortality
++; /* skipped above when how==QUIT */
1047 Strcpy(killer
.name
, "quit while already on Charon's boat");
1050 if (how
== ESCAPED
|| how
== PANICKED
)
1051 killer
.format
= NO_KILLER_PREFIX
;
1053 fixup_death(how
); /* actually, fixup multi_reason */
1055 if (how
!= PANICKED
) {
1056 /* these affect score and/or bones, but avoid them during panic */
1057 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1061 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1066 display_nhwindow(WIN_MESSAGE
, FALSE
);
1068 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1069 disclose(how
, taken
);
1071 /* finish_paybill should be called after disclosure but before bones */
1072 if (bones_ok
&& taken
)
1075 /* grave creation should be after disclosure so it doesn't have
1076 this grave in the current level's features for #overview */
1077 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1078 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1079 int mnum
= u
.umonnum
;
1082 /* Base corpse on race when not poly'd since original
1083 * u.umonnum is based on role, and all role monsters
1086 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1090 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1091 Sprintf(pbuf
, "%s, ", plname
);
1092 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1093 make_grave(u
.ux
, u
.uy
, pbuf
);
1095 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1097 /* calculate score, before creating bones [container gold] */
1099 int deepest
= deepest_lev_reached(FALSE
);
1101 umoney
= money_cnt(invent
);
1103 umoney
+= hidden_gold(); /* accumulate gold from containers */
1104 tmp
= umoney
- tmp
; /* net gain */
1110 tmp
+= 50L * (long) (deepest
- 1);
1112 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1113 nowrap_add(u
.urexp
, tmp
);
1115 /* ascension gives a score bonus iff offering to original deity */
1116 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1117 /* retaining original alignment: score *= 2;
1118 converting, then using helm-of-OA to switch back: *= 1.5 */
1119 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1122 nowrap_add(u
.urexp
, tmp
);
1126 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1127 /* give this feedback even if bones aren't going to be created,
1128 so that its presence or absence doesn't tip off the player to
1129 new bones or their lack; it might be a lie if makemon fails */
1130 Your("body rises from the dead as %s...",
1131 an(mons
[u
.ugrave_arise
].mname
));
1132 display_nhwindow(WIN_MESSAGE
, FALSE
);
1136 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1137 savebones(how
, endtime
, corpse
);
1138 /* corpse may be invalid pointer now so
1139 ensure that it isn't used again */
1140 corpse
= (struct obj
*) 0;
1143 /* update gold for the rip output, which can't use hidden_gold()
1144 (containers will be gone by then if bones just got saved...) */
1145 done_money
= umoney
;
1147 /* clean up unneeded windows */
1150 free_pickinv_cache(); /* extra persistent window if perm_invent */
1151 if (WIN_INVEN
!= WIN_ERR
)
1152 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1153 display_nhwindow(WIN_MESSAGE
, TRUE
);
1154 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1155 #ifndef STATUS_VIA_WINDOWPORT
1156 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1158 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1160 if (!done_stopprint
|| flags
.tombstone
)
1161 endwin
= create_nhwindow(NHW_TEXT
);
1163 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1164 outrip(endwin
, how
, endtime
);
1166 done_stopprint
= 1; /* just avoid any more output */
1168 if (u
.uhave
.amulet
) {
1169 Strcat(killer
.name
, " (with the Amulet)");
1170 } else if (how
== ESCAPED
) {
1171 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1172 Strcat(killer
.name
, " (in celestial disgrace)");
1173 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1174 Strcat(killer
.name
, " (with a fake Amulet)");
1175 /* don't bother counting to see whether it should be plural */
1178 if (!done_stopprint
) {
1179 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1181 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1184 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1185 putstr(endwin
, 0, pbuf
);
1186 putstr(endwin
, 0, "");
1189 if (how
== ESCAPED
|| how
== ASCENDED
) {
1192 register struct val_list
*val
;
1195 for (val
= valuables
; val
->list
; val
++)
1196 for (i
= 0; i
< val
->size
; i
++) {
1197 val
->list
[i
].count
= 0L;
1199 get_valuables(invent
);
1201 /* add points for collected valuables */
1202 for (val
= valuables
; val
->list
; val
++)
1203 for (i
= 0; i
< val
->size
; i
++)
1204 if (val
->list
[i
].count
!= 0L) {
1205 tmp
= val
->list
[i
].count
1206 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1207 nowrap_add(u
.urexp
, tmp
);
1210 /* count the points for artifacts */
1211 artifact_score(invent
, TRUE
, endwin
);
1213 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1215 if (!done_stopprint
)
1216 Strcpy(pbuf
, "You");
1217 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1218 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1219 if (Schroedingers_cat
) {
1220 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1222 nowrap_add(u
.urexp
, mhp
);
1223 if (!done_stopprint
)
1224 Strcat(eos(pbuf
), " and Schroedinger's cat");
1228 if (!done_stopprint
)
1229 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1231 nowrap_add(u
.urexp
, mtmp
->mhp
);
1234 if (!done_stopprint
)
1235 putstr(endwin
, 0, pbuf
);
1238 if (!done_stopprint
)
1241 if (!done_stopprint
) {
1242 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1243 how
== ASCENDED
? "went to your reward"
1244 : "escaped from the dungeon",
1245 u
.urexp
, plur(u
.urexp
));
1246 putstr(endwin
, 0, pbuf
);
1249 if (!done_stopprint
)
1250 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1252 /* list valuables here */
1253 for (val
= valuables
; val
->list
; val
++) {
1254 sort_valuables(val
->list
, val
->size
);
1255 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1256 int typ
= val
->list
[i
].typ
;
1257 long count
= val
->list
[i
].count
;
1261 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1262 otmp
= mksobj(typ
, FALSE
, FALSE
);
1263 makeknown(otmp
->otyp
);
1264 otmp
->known
= 1; /* for fake amulets */
1265 otmp
->dknown
= 1; /* seen it (blindness fix) */
1266 if (has_oname(otmp
))
1269 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1270 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1272 obfree(otmp
, (struct obj
*) 0);
1274 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1275 count
, plur(count
));
1277 putstr(endwin
, 0, pbuf
);
1281 } else if (!done_stopprint
) {
1282 /* did not escape or ascend */
1283 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1284 /* level teleported out of the dungeon; `how' is DIED,
1285 due to falling or to "arriving at heaven prematurely" */
1286 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1287 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1289 /* more conventional demise */
1290 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1292 if (Is_astralevel(&u
.uz
))
1293 where
= "The Astral Plane";
1294 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1295 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1296 Sprintf(eos(pbuf
), " on dungeon level %d",
1297 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1300 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1301 putstr(endwin
, 0, pbuf
);
1304 if (!done_stopprint
) {
1305 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1306 plur(umoney
), moves
, plur(moves
));
1307 putstr(endwin
, 0, pbuf
);
1309 if (!done_stopprint
) {
1311 "You were level %d with a maximum of %d hit point%s when you %s.",
1312 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1313 putstr(endwin
, 0, pbuf
);
1314 putstr(endwin
, 0, "");
1316 if (!done_stopprint
)
1317 display_nhwindow(endwin
, TRUE
);
1318 if (endwin
!= WIN_ERR
)
1319 destroy_nhwindow(endwin
);
1321 /* "So when I die, the first thing I will see in Heaven is a
1323 if (have_windows
&& !iflags
.toptenwin
)
1324 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1325 topten(how
, endtime
);
1327 exit_nhwindows((char *) 0);
1329 if (done_stopprint
) {
1333 terminate(EXIT_SUCCESS
);
1337 container_contents(list
, identified
, all_containers
, reportempty
)
1339 boolean identified
, all_containers
, reportempty
;
1341 register struct obj
*box
, *obj
;
1343 boolean cat
, deadcat
;
1345 for (box
= list
; box
; box
= box
->nobj
) {
1346 if (Is_container(box
) || box
->otyp
== STATUE
) {
1347 box
->cknown
= 1; /* we're looking at the contents now */
1350 cat
= deadcat
= FALSE
;
1351 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1352 /* Schroedinger's Cat? */
1353 cat
= odds_and_ends(box
, CAT_CHECK
);
1355 Schroedingers_cat
= TRUE
;
1360 if (box
->otyp
== BAG_OF_TRICKS
) {
1361 continue; /* wrong type of container */
1362 } else if (box
->cobj
) {
1363 winid tmpwin
= create_nhwindow(NHW_MENU
);
1365 sortloot(&box
->cobj
,
1366 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1367 ? SORTLOOT_LOOT
: 0)
1368 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1370 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1371 putstr(tmpwin
, 0, buf
);
1372 putstr(tmpwin
, 0, "");
1373 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1375 makeknown(obj
->otyp
);
1376 obj
->known
= obj
->bknown
= obj
->dknown
1378 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1379 obj
->cknown
= obj
->lknown
= 1;
1381 putstr(tmpwin
, 0, doname(obj
));
1384 putstr(tmpwin
, 0, "Schroedinger's cat");
1386 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1387 display_nhwindow(tmpwin
, TRUE
);
1388 destroy_nhwindow(tmpwin
);
1390 container_contents(box
->cobj
, identified
, TRUE
,
1392 } else if (cat
|| deadcat
) {
1393 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1394 deadcat
? "dead " : "");
1395 display_nhwindow(WIN_MESSAGE
, FALSE
);
1396 } else if (reportempty
) {
1397 pline("%s is empty.", upstart(thesimpleoname(box
)));
1398 display_nhwindow(WIN_MESSAGE
, FALSE
);
1401 if (!all_containers
)
1406 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1411 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1413 getreturn("to exit");
1415 /* don't bother to try to release memory if we're in panic mode, to
1416 avoid trouble in case that happens to be due to memory problems */
1417 if (!program_state
.panicking
) {
1424 * This is liable to draw a warning if compiled with gcc, but it's
1425 * more important to flag panic() -> really_done() -> terminate()
1426 * as __noreturn__ then to avoid the warning.
1428 /* don't call exit() if already executing within an exit handler;
1429 that would cancel any other pending user-mode handlers */
1430 if (program_state
.exiting
)
1433 program_state
.exiting
= 1;
1434 nethack_exit(status
);
1437 extern const int monstr
[];
1439 static const char *vanqorders
[] = {
1440 "traditional: by monster level, by internal monster index",
1441 #define VANQ_MLVL_MNDX 0
1442 "by monster toughness, by internal monster index",
1443 #define VANQ_MSTR_MNDX 1
1444 "alphabetically, first unique monsters, then others",
1445 #define VANQ_ALPHA_SEP 2
1446 "alphabetically, unique monsters and others intermixed",
1447 #define VANQ_ALPHA_MIX 3
1448 "by monster class, high to low level within class",
1449 #define VANQ_MCLS_HTOL 4
1450 "by monster class, low to high level within class",
1451 #define VANQ_MCLS_LTOH 5
1452 "by count, high to low, by internal index within tied count",
1453 #define VANQ_COUNT_H_L 6
1454 "by count, low to high, by internal index within tied count",
1455 #define VANQ_COUNT_L_H 7
1457 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1459 STATIC_PTR
int CFDECLSPEC
1460 vanqsort_cmp(vptr1
, vptr2
)
1461 const genericptr vptr1
;
1462 const genericptr vptr2
;
1464 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1465 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1466 const char *name1
, *name2
, *punct
;
1469 switch (vanq_sortmode
) {
1471 case VANQ_MLVL_MNDX
:
1472 /* sort by monster level */
1473 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1474 res
= mlev2
- mlev1
; /* mlevel high to low */
1476 case VANQ_MSTR_MNDX
:
1477 /* sort by monster toughness */
1478 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1479 res
= mstr2
- mstr1
; /* monstr high to low */
1481 case VANQ_ALPHA_SEP
:
1482 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1483 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1484 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1485 res
= uniq2
- uniq1
;
1487 } /* else both unique or neither unique */
1489 case VANQ_ALPHA_MIX
:
1490 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1491 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1493 case VANQ_MCLS_HTOL
:
1494 case VANQ_MCLS_LTOH
:
1495 /* mons[].mlet is a small integer, 1..N, of type plain char;
1496 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1497 an inappropriate result when mlet2 is greater than mlet1,
1498 so force our copies (mcls1, mcls2) to be signed */
1499 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1500 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1501 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1502 punctuation characters are used for classes beyond those */
1503 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1504 /* force a specific order to the punctuation classes that's
1505 different from the internal order;
1506 internal order is ok if neither or just one is punctuation
1507 since letters have lower values so come out before punct */
1508 static const char punctclasses
[] = {
1509 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1512 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1513 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1514 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1515 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1517 res
= mcls1
- mcls2
; /* class */
1519 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1520 res
= mlev1
- mlev2
; /* mlevel low to high */
1521 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1522 res
= -res
; /* mlevel high to low */
1525 case VANQ_COUNT_H_L
:
1526 case VANQ_COUNT_L_H
:
1527 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1528 res
= died2
- died1
; /* dead count high to low */
1529 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1530 res
= -res
; /* dead count low to high */
1533 /* tiebreaker: internal mons[] index */
1535 res
= indx1
- indx2
; /* mndx low to high */
1539 /* returns -1 if cancelled via ESC */
1544 menu_item
*selected
;
1548 tmpwin
= create_nhwindow(NHW_MENU
);
1550 any
= zeroany
; /* zero out all bits */
1551 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1552 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1555 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1556 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1558 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1560 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1561 destroy_nhwindow(tmpwin
);
1563 choice
= selected
[0].item
.a_int
- 1;
1564 /* skip preselected entry if we have more than one item chosen */
1565 if (n
> 1 && choice
== vanq_sortmode
)
1566 choice
= selected
[1].item
.a_int
- 1;
1567 free((genericptr_t
) selected
);
1568 vanq_sortmode
= choice
;
1570 return (n
< 0) ? -1 : vanq_sortmode
;
1573 /* #vanquished command */
1577 list_vanquished('a', FALSE
);
1581 /* high priests aren't unique but are flagged as such to simplify something */
1582 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1583 && mndx != PM_HIGH_PRIEST)
1586 list_vanquished(defquery
, ask
)
1592 unsigned ntypes
, ni
;
1593 long total_killed
= 0L;
1595 short mindx
[NUMMONS
];
1596 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1598 /* get totals first */
1600 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1601 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1603 mindx
[ntypes
++] = i
;
1604 total_killed
+= (long) nkilled
;
1607 /* vanquished creatures list;
1608 * includes all dead monsters, not just those killed by the player
1611 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1612 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1614 c
= ask
? yn_function(
1615 "Do you want an account of creatures vanquished?",
1616 ynaqchars
, defquery
)
1620 if (c
== 'y' || c
== 'a') {
1621 if (c
== 'a') { /* ask player to choose sort order */
1622 /* choose value for vanq_sortmode via menu; ESC cancels list
1623 of vanquished monsters but does not set 'done_stopprint' */
1624 if (set_vanq_order() < 0)
1627 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1628 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1629 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1631 klwin
= create_nhwindow(NHW_MENU
);
1632 putstr(klwin
, 0, "Vanquished creatures:");
1633 putstr(klwin
, 0, "");
1635 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1636 for (ni
= 0; ni
< ntypes
; ni
++) {
1638 nkilled
= mvitals
[i
].died
;
1639 mlet
= mons
[i
].mlet
;
1640 if (class_header
&& mlet
!= prev_mlet
) {
1641 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1642 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1646 if (UniqCritterIndx(i
)) {
1647 Sprintf(buf
, "%s%s",
1648 !type_is_pname(&mons
[i
]) ? "the " : "",
1653 Sprintf(eos(buf
), " (twice)");
1656 Sprintf(eos(buf
), " (thrice)");
1659 Sprintf(eos(buf
), " (%d times)", nkilled
);
1665 if (uniq_header
&& was_uniq
) {
1666 putstr(klwin
, 0, "");
1669 /* trolls or undead might have come back,
1670 but we don't keep track of that */
1672 Strcpy(buf
, an(mons
[i
].mname
));
1674 Sprintf(buf
, "%3d %s", nkilled
,
1675 makeplural(mons
[i
].mname
));
1677 /* number of leading spaces to match 3 digit prefix */
1678 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1679 : !strncmpi(buf
, "an ", 3) ? 1
1680 : !strncmpi(buf
, "a ", 2) ? 2
1681 : !isdigit(buf
[2]) ? 4 : 0;
1684 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1685 putstr(klwin
, 0, buftoo
);
1688 * if (Hallucination)
1689 * putstr(klwin, 0, "and a partridge in a pear tree");
1692 putstr(klwin
, 0, "");
1693 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1694 putstr(klwin
, 0, buf
);
1696 display_nhwindow(klwin
, TRUE
);
1697 destroy_nhwindow(klwin
);
1699 } else if (defquery
== 'a') {
1700 /* #dovanquished rather than final disclosure, so pline() is ok */
1701 pline("No monsters have been vanquished.");
1705 /* number of monster species which have been genocided */
1711 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1712 if (mvitals
[i
].mvflags
& G_GENOD
) {
1714 if (UniqCritterIndx(i
))
1715 impossible("unique creature '%d: %s' genocided?",
1727 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1728 if (UniqCritterIndx(i
))
1730 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1737 list_genocided(defquery
, ask
)
1742 int ngenocided
, nextinct
;
1747 ngenocided
= num_genocides();
1748 nextinct
= num_extinct();
1750 /* genocided or extinct species list */
1751 if (ngenocided
!= 0 || nextinct
!= 0) {
1752 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1753 (nextinct
&& !ngenocided
) ? "extinct " : "",
1754 (ngenocided
) ? " genocided" : "",
1755 (nextinct
&& ngenocided
) ? " and extinct" : "");
1756 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1760 klwin
= create_nhwindow(NHW_MENU
);
1761 Sprintf(buf
, "%s%s species:",
1762 (ngenocided
) ? "Genocided" : "Extinct",
1763 (nextinct
&& ngenocided
) ? " or extinct" : "");
1764 putstr(klwin
, 0, buf
);
1765 putstr(klwin
, 0, "");
1767 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1768 /* uniques can't be genocided but can become extinct;
1769 however, they're never reported as extinct, so skip them */
1770 if (UniqCritterIndx(i
))
1772 if (mvitals
[i
].mvflags
& G_GONE
) {
1773 Strcpy(buf
, makeplural(mons
[i
].mname
));
1775 * "Extinct" is unfortunate terminology. A species
1776 * is marked extinct when its birth limit is reached,
1777 * but there might be members of the species still
1778 * alive, contradicting the meaning of the word.
1780 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1781 Strcat(buf
, " (extinct)");
1782 putstr(klwin
, 0, buf
);
1785 putstr(klwin
, 0, "");
1786 if (ngenocided
> 0) {
1787 Sprintf(buf
, "%d species genocided.", ngenocided
);
1788 putstr(klwin
, 0, buf
);
1791 Sprintf(buf
, "%d species extinct.", nextinct
);
1792 putstr(klwin
, 0, buf
);
1795 display_nhwindow(klwin
, TRUE
);
1796 destroy_nhwindow(klwin
);
1801 /* set a delayed killer, ensure non-delayed killer is cleared out */
1803 delayed_killer(id
, format
, killername
)
1806 const char *killername
;
1808 struct kinfo
*k
= find_delayed_killer(id
);
1810 if (k
== (struct kinfo
*) 0) {
1811 /* no match, add a new delayed killer to the list */
1812 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1814 k
->next
= killer
.next
;
1819 Strcpy(k
->name
, killername
? killername
: "");
1824 find_delayed_killer(id
)
1829 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1837 dealloc_killer(kptr
)
1840 struct kinfo
*prev
= &killer
, *k
;
1842 if (kptr
== (struct kinfo
*) 0)
1844 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1850 if (k
== (struct kinfo
*) 0) {
1851 impossible("dealloc_killer not on list");
1853 prev
->next
= k
->next
;
1854 free((genericptr_t
) k
);
1859 save_killers(fd
, mode
)
1865 if (perform_bwrite(mode
)) {
1866 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1867 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1870 if (release_data(mode
)) {
1871 while (killer
.next
) {
1872 kptr
= killer
.next
->next
;
1873 free((genericptr_t
) killer
.next
);
1885 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1886 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1888 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1900 while (*p
&& isspace((uchar
) *p
))
1904 while (*p
&& !isspace((uchar
) *p
))
1916 out
+= strlen(out
); /* eos() */
1917 while (*in
&& isspace((uchar
) *in
))
1919 while (*in
&& !isspace((uchar
) *in
))
1926 build_english_list(in
)
1930 int len
= (int) strlen(p
), words
= wordcount(p
);
1932 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
1934 len
+= 3 + (words
- 1);
1935 out
= (char *) alloc(len
+ 1);
1936 *out
= '\0'; /* bel_copy1() appends */
1940 impossible("no words in list");
1948 /* "first or second" */
1952 /* "first, second, or third */
1956 } while (--words
> 1);