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
;
784 if (!context
.mon_moving
)
789 * Get valuables from the given list. Revised code: the list always remains
794 struct obj
*list
; /* inventory or container contents */
796 register struct obj
*obj
;
799 /* find amulets and gems, ignoring all artifacts */
800 for (obj
= list
; obj
; obj
= obj
->nobj
)
801 if (Has_contents(obj
)) {
802 get_valuables(obj
->cobj
);
803 } else if (obj
->oartifact
) {
805 } else if (obj
->oclass
== AMULET_CLASS
) {
806 i
= obj
->otyp
- FIRST_AMULET
;
807 if (!amulets
[i
].count
) {
808 amulets
[i
].count
= obj
->quan
;
809 amulets
[i
].typ
= obj
->otyp
;
811 amulets
[i
].count
+= obj
->quan
; /* always adds one */
812 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
813 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
814 if (!gems
[i
].count
) {
815 gems
[i
].count
= obj
->quan
;
816 gems
[i
].typ
= obj
->otyp
;
818 gems
[i
].count
+= obj
->quan
;
824 * Sort collected valuables, most frequent to least. We could just
825 * as easily use qsort, but we don't care about efficiency here.
828 sort_valuables(list
, size
)
829 struct valuable_data list
[];
830 int size
; /* max value is less than 20 */
833 struct valuable_data ltmp
;
835 /* move greater quantities to the front of the list */
836 for (i
= 1; i
< size
; i
++) {
837 if (list
[i
].count
== 0)
838 continue; /* empty slot */
839 ltmp
= list
[i
]; /* structure copy */
840 for (j
= i
; j
> 0; --j
)
841 if (list
[j
- 1].count
>= ltmp
.count
)
844 list
[j
] = list
[j
- 1];
854 odds_and_ends(list
, what
)
859 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
861 case CAT_CHECK
: /* Schroedinger's Cat */
862 /* Ascending is deterministic */
863 if (SchroedingersBox(otmp
))
867 if (Has_contents(otmp
))
868 return odds_and_ends(otmp
->cobj
, what
);
873 /* called twice; first to calculate total, then to list relevant items */
875 artifact_score(list
, counting
, endwin
)
877 boolean counting
; /* true => add up points; false => display them */
883 short dummy
; /* object type returned by artifact_name() */
885 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
886 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
887 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
888 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
889 value
= arti_cost(otmp
); /* zorkmid value */
890 points
= value
* 5 / 2; /* score value */
892 nowrap_add(u
.urexp
, points
);
894 makeknown(otmp
->otyp
);
895 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
896 /* assumes artifacts don't have quan > 1 */
897 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
898 the_unique_obj(otmp
) ? "The " : "",
899 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
900 : OBJ_NAME(objects
[otmp
->otyp
]),
901 value
, currency(value
), points
);
902 putstr(endwin
, 0, pbuf
);
905 if (Has_contents(otmp
))
906 artifact_score(otmp
->cobj
, counting
, endwin
);
910 /* Be careful not to call panic from here! */
915 if (how
== TRICKED
) {
916 if (killer
.name
[0]) {
917 paniclog("trickery", killer
.name
);
921 You("are a very tricky wizard, it seems.");
926 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
927 killer
.format
= NO_KILLER_PREFIX
;
928 /* Avoid killed by "a" burning or "a" starvation */
929 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
930 killer
.format
= KILLED_BY
;
931 if (!killer
.name
[0] || how
>= PANICKED
)
932 Strcpy(killer
.name
, deaths
[how
]);
936 if (Lifesaved
&& (how
<= GENOCIDED
)) {
937 pline("But wait...");
938 makeknown(AMULET_OF_LIFE_SAVING
);
939 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
942 You_feel("much better!");
943 pline_The("medallion crumbles to dust!");
947 (void) adjattrib(A_CON
, -1, TRUE
);
949 if (how
== GENOCIDED
) {
950 pline("Unfortunately you are still genocided...");
957 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
958 && !paranoid_query(ParanoidDie
, "Die?")) {
959 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
968 /* separated from done() in order to specify the __noreturn__ attribute */
975 winid endwin
= WIN_ERR
;
976 boolean bones_ok
, have_windows
= iflags
.window_inited
;
977 struct obj
*corpse
= (struct obj
*) 0;
983 * The game is now over...
985 program_state
.gameover
= 1;
986 /* in case of a subsequent panic(), there's no point trying to save */
987 program_state
.something_worth_saving
= 0;
988 /* render vision subsystem inoperative */
989 iflags
.vision_inited
= 0;
991 /* might have been killed while using a disposable item, so make sure
992 it's gone prior to inventory disclosure and creation of bones data */
994 /* maybe not on object lists; if an active light source, would cause
995 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
996 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
997 dealloc_obj(thrownobj
);
998 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
999 dealloc_obj(kickedobj
);
1001 /* remember time of death here instead of having bones, rip, and
1002 topten figure it out separately and possibly getting different
1003 time or even day if player is slow responding to --More-- */
1004 urealtime
.finish_time
= endtime
= getnow();
1005 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1007 /* Sometimes you die on the first move. Life's not fair.
1008 * On those rare occasions you get hosed immediately, go out
1009 * smiling... :-) -3.
1011 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1012 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1015 wait_synch(); /* flush screen output */
1017 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1018 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1019 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1020 sethanguphandler(done_hangup
);
1022 #endif /* NO_SIGNAL */
1024 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1026 if (bones_ok
&& launch_in_progress())
1027 force_launch_placement();
1029 /* maintain ugrave_arise even for !bones_ok */
1030 if (how
== PANICKED
)
1031 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1032 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1033 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1034 else if (how
== STONING
)
1035 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1036 else if (how
== TURNED_SLIME
)
1037 u
.ugrave_arise
= PM_GREEN_SLIME
;
1039 /* if pets will contribute to score, populate mydogs list now
1040 (bones creation isn't a factor, but pline() messaging is) */
1041 if (how
== ESCAPED
|| how
== ASCENDED
)
1045 killer
.format
= NO_KILLER_PREFIX
;
1048 u
.umortality
++; /* skipped above when how==QUIT */
1049 Strcpy(killer
.name
, "quit while already on Charon's boat");
1052 if (how
== ESCAPED
|| how
== PANICKED
)
1053 killer
.format
= NO_KILLER_PREFIX
;
1055 fixup_death(how
); /* actually, fixup multi_reason */
1057 if (how
!= PANICKED
) {
1058 /* these affect score and/or bones, but avoid them during panic */
1059 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1063 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1068 display_nhwindow(WIN_MESSAGE
, FALSE
);
1070 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1071 disclose(how
, taken
);
1073 /* finish_paybill should be called after disclosure but before bones */
1074 if (bones_ok
&& taken
)
1077 /* grave creation should be after disclosure so it doesn't have
1078 this grave in the current level's features for #overview */
1079 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1080 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1081 int mnum
= u
.umonnum
;
1084 /* Base corpse on race when not poly'd since original
1085 * u.umonnum is based on role, and all role monsters
1088 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1092 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1093 Sprintf(pbuf
, "%s, ", plname
);
1094 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1095 make_grave(u
.ux
, u
.uy
, pbuf
);
1097 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1099 /* calculate score, before creating bones [container gold] */
1101 int deepest
= deepest_lev_reached(FALSE
);
1103 umoney
= money_cnt(invent
);
1105 umoney
+= hidden_gold(); /* accumulate gold from containers */
1106 tmp
= umoney
- tmp
; /* net gain */
1112 tmp
+= 50L * (long) (deepest
- 1);
1114 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1115 nowrap_add(u
.urexp
, tmp
);
1117 /* ascension gives a score bonus iff offering to original deity */
1118 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1119 /* retaining original alignment: score *= 2;
1120 converting, then using helm-of-OA to switch back: *= 1.5 */
1121 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1124 nowrap_add(u
.urexp
, tmp
);
1128 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1129 /* give this feedback even if bones aren't going to be created,
1130 so that its presence or absence doesn't tip off the player to
1131 new bones or their lack; it might be a lie if makemon fails */
1132 Your("body rises from the dead as %s...",
1133 an(mons
[u
.ugrave_arise
].mname
));
1134 display_nhwindow(WIN_MESSAGE
, FALSE
);
1138 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1139 savebones(how
, endtime
, corpse
);
1140 /* corpse may be invalid pointer now so
1141 ensure that it isn't used again */
1142 corpse
= (struct obj
*) 0;
1145 /* update gold for the rip output, which can't use hidden_gold()
1146 (containers will be gone by then if bones just got saved...) */
1147 done_money
= umoney
;
1149 /* clean up unneeded windows */
1152 free_pickinv_cache(); /* extra persistent window if perm_invent */
1153 if (WIN_INVEN
!= WIN_ERR
)
1154 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1155 display_nhwindow(WIN_MESSAGE
, TRUE
);
1156 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1157 #ifndef STATUS_VIA_WINDOWPORT
1158 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1160 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1162 if (!done_stopprint
|| flags
.tombstone
)
1163 endwin
= create_nhwindow(NHW_TEXT
);
1165 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1166 outrip(endwin
, how
, endtime
);
1168 done_stopprint
= 1; /* just avoid any more output */
1170 if (u
.uhave
.amulet
) {
1171 Strcat(killer
.name
, " (with the Amulet)");
1172 } else if (how
== ESCAPED
) {
1173 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1174 Strcat(killer
.name
, " (in celestial disgrace)");
1175 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1176 Strcat(killer
.name
, " (with a fake Amulet)");
1177 /* don't bother counting to see whether it should be plural */
1180 if (!done_stopprint
) {
1181 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1183 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1186 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1187 putstr(endwin
, 0, pbuf
);
1188 putstr(endwin
, 0, "");
1191 if (how
== ESCAPED
|| how
== ASCENDED
) {
1194 register struct val_list
*val
;
1197 for (val
= valuables
; val
->list
; val
++)
1198 for (i
= 0; i
< val
->size
; i
++) {
1199 val
->list
[i
].count
= 0L;
1201 get_valuables(invent
);
1203 /* add points for collected valuables */
1204 for (val
= valuables
; val
->list
; val
++)
1205 for (i
= 0; i
< val
->size
; i
++)
1206 if (val
->list
[i
].count
!= 0L) {
1207 tmp
= val
->list
[i
].count
1208 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1209 nowrap_add(u
.urexp
, tmp
);
1212 /* count the points for artifacts */
1213 artifact_score(invent
, TRUE
, endwin
);
1215 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1217 if (!done_stopprint
)
1218 Strcpy(pbuf
, "You");
1219 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1220 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1221 if (Schroedingers_cat
) {
1222 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1224 nowrap_add(u
.urexp
, mhp
);
1225 if (!done_stopprint
)
1226 Strcat(eos(pbuf
), " and Schroedinger's cat");
1230 if (!done_stopprint
)
1231 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1233 nowrap_add(u
.urexp
, mtmp
->mhp
);
1236 if (!done_stopprint
)
1237 putstr(endwin
, 0, pbuf
);
1240 if (!done_stopprint
)
1243 if (!done_stopprint
) {
1244 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1245 how
== ASCENDED
? "went to your reward"
1246 : "escaped from the dungeon",
1247 u
.urexp
, plur(u
.urexp
));
1248 putstr(endwin
, 0, pbuf
);
1251 if (!done_stopprint
)
1252 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1254 /* list valuables here */
1255 for (val
= valuables
; val
->list
; val
++) {
1256 sort_valuables(val
->list
, val
->size
);
1257 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1258 int typ
= val
->list
[i
].typ
;
1259 long count
= val
->list
[i
].count
;
1263 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1264 otmp
= mksobj(typ
, FALSE
, FALSE
);
1265 makeknown(otmp
->otyp
);
1266 otmp
->known
= 1; /* for fake amulets */
1267 otmp
->dknown
= 1; /* seen it (blindness fix) */
1268 if (has_oname(otmp
))
1271 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1272 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1274 obfree(otmp
, (struct obj
*) 0);
1276 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1277 count
, plur(count
));
1279 putstr(endwin
, 0, pbuf
);
1283 } else if (!done_stopprint
) {
1284 /* did not escape or ascend */
1285 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1286 /* level teleported out of the dungeon; `how' is DIED,
1287 due to falling or to "arriving at heaven prematurely" */
1288 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1289 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1291 /* more conventional demise */
1292 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1294 if (Is_astralevel(&u
.uz
))
1295 where
= "The Astral Plane";
1296 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1297 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1298 Sprintf(eos(pbuf
), " on dungeon level %d",
1299 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1302 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1303 putstr(endwin
, 0, pbuf
);
1306 if (!done_stopprint
) {
1307 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1308 plur(umoney
), moves
, plur(moves
));
1309 putstr(endwin
, 0, pbuf
);
1311 if (!done_stopprint
) {
1313 "You were level %d with a maximum of %d hit point%s when you %s.",
1314 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1315 putstr(endwin
, 0, pbuf
);
1316 putstr(endwin
, 0, "");
1318 if (!done_stopprint
)
1319 display_nhwindow(endwin
, TRUE
);
1320 if (endwin
!= WIN_ERR
)
1321 destroy_nhwindow(endwin
);
1323 /* "So when I die, the first thing I will see in Heaven is a
1325 if (have_windows
&& !iflags
.toptenwin
)
1326 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1327 topten(how
, endtime
);
1329 exit_nhwindows((char *) 0);
1331 if (done_stopprint
) {
1335 terminate(EXIT_SUCCESS
);
1339 container_contents(list
, identified
, all_containers
, reportempty
)
1341 boolean identified
, all_containers
, reportempty
;
1343 register struct obj
*box
, *obj
;
1345 boolean cat
, deadcat
;
1347 for (box
= list
; box
; box
= box
->nobj
) {
1348 if (Is_container(box
) || box
->otyp
== STATUE
) {
1349 box
->cknown
= 1; /* we're looking at the contents now */
1352 cat
= deadcat
= FALSE
;
1353 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1354 /* Schroedinger's Cat? */
1355 cat
= odds_and_ends(box
, CAT_CHECK
);
1357 Schroedingers_cat
= TRUE
;
1362 if (box
->otyp
== BAG_OF_TRICKS
) {
1363 continue; /* wrong type of container */
1364 } else if (box
->cobj
) {
1365 winid tmpwin
= create_nhwindow(NHW_MENU
);
1367 sortloot(&box
->cobj
,
1368 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1369 ? SORTLOOT_LOOT
: 0)
1370 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1372 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1373 putstr(tmpwin
, 0, buf
);
1374 putstr(tmpwin
, 0, "");
1375 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1377 makeknown(obj
->otyp
);
1378 obj
->known
= obj
->bknown
= obj
->dknown
1380 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1381 obj
->cknown
= obj
->lknown
= 1;
1383 putstr(tmpwin
, 0, doname(obj
));
1386 putstr(tmpwin
, 0, "Schroedinger's cat");
1388 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1389 display_nhwindow(tmpwin
, TRUE
);
1390 destroy_nhwindow(tmpwin
);
1392 container_contents(box
->cobj
, identified
, TRUE
,
1394 } else if (cat
|| deadcat
) {
1395 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1396 deadcat
? "dead " : "");
1397 display_nhwindow(WIN_MESSAGE
, FALSE
);
1398 } else if (reportempty
) {
1399 pline("%s is empty.", upstart(thesimpleoname(box
)));
1400 display_nhwindow(WIN_MESSAGE
, FALSE
);
1403 if (!all_containers
)
1408 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1413 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1415 getreturn("to exit");
1417 /* don't bother to try to release memory if we're in panic mode, to
1418 avoid trouble in case that happens to be due to memory problems */
1419 if (!program_state
.panicking
) {
1426 * This is liable to draw a warning if compiled with gcc, but it's
1427 * more important to flag panic() -> really_done() -> terminate()
1428 * as __noreturn__ then to avoid the warning.
1430 /* don't call exit() if already executing within an exit handler;
1431 that would cancel any other pending user-mode handlers */
1432 if (program_state
.exiting
)
1435 program_state
.exiting
= 1;
1436 nethack_exit(status
);
1439 extern const int monstr
[];
1441 static const char *vanqorders
[] = {
1442 "traditional: by monster level, by internal monster index",
1443 #define VANQ_MLVL_MNDX 0
1444 "by monster toughness, by internal monster index",
1445 #define VANQ_MSTR_MNDX 1
1446 "alphabetically, first unique monsters, then others",
1447 #define VANQ_ALPHA_SEP 2
1448 "alphabetically, unique monsters and others intermixed",
1449 #define VANQ_ALPHA_MIX 3
1450 "by monster class, high to low level within class",
1451 #define VANQ_MCLS_HTOL 4
1452 "by monster class, low to high level within class",
1453 #define VANQ_MCLS_LTOH 5
1454 "by count, high to low, by internal index within tied count",
1455 #define VANQ_COUNT_H_L 6
1456 "by count, low to high, by internal index within tied count",
1457 #define VANQ_COUNT_L_H 7
1459 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1461 STATIC_PTR
int CFDECLSPEC
1462 vanqsort_cmp(vptr1
, vptr2
)
1463 const genericptr vptr1
;
1464 const genericptr vptr2
;
1466 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1467 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1468 const char *name1
, *name2
, *punct
;
1471 switch (vanq_sortmode
) {
1473 case VANQ_MLVL_MNDX
:
1474 /* sort by monster level */
1475 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1476 res
= mlev2
- mlev1
; /* mlevel high to low */
1478 case VANQ_MSTR_MNDX
:
1479 /* sort by monster toughness */
1480 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1481 res
= mstr2
- mstr1
; /* monstr high to low */
1483 case VANQ_ALPHA_SEP
:
1484 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1485 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1486 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1487 res
= uniq2
- uniq1
;
1489 } /* else both unique or neither unique */
1491 case VANQ_ALPHA_MIX
:
1492 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1493 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1495 case VANQ_MCLS_HTOL
:
1496 case VANQ_MCLS_LTOH
:
1497 /* mons[].mlet is a small integer, 1..N, of type plain char;
1498 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1499 an inappropriate result when mlet2 is greater than mlet1,
1500 so force our copies (mcls1, mcls2) to be signed */
1501 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1502 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1503 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1504 punctuation characters are used for classes beyond those */
1505 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1506 /* force a specific order to the punctuation classes that's
1507 different from the internal order;
1508 internal order is ok if neither or just one is punctuation
1509 since letters have lower values so come out before punct */
1510 static const char punctclasses
[] = {
1511 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1514 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1515 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1516 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1517 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1519 res
= mcls1
- mcls2
; /* class */
1521 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1522 res
= mlev1
- mlev2
; /* mlevel low to high */
1523 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1524 res
= -res
; /* mlevel high to low */
1527 case VANQ_COUNT_H_L
:
1528 case VANQ_COUNT_L_H
:
1529 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1530 res
= died2
- died1
; /* dead count high to low */
1531 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1532 res
= -res
; /* dead count low to high */
1535 /* tiebreaker: internal mons[] index */
1537 res
= indx1
- indx2
; /* mndx low to high */
1541 /* returns -1 if cancelled via ESC */
1546 menu_item
*selected
;
1550 tmpwin
= create_nhwindow(NHW_MENU
);
1552 any
= zeroany
; /* zero out all bits */
1553 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1554 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1557 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1558 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1560 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1562 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1563 destroy_nhwindow(tmpwin
);
1565 choice
= selected
[0].item
.a_int
- 1;
1566 /* skip preselected entry if we have more than one item chosen */
1567 if (n
> 1 && choice
== vanq_sortmode
)
1568 choice
= selected
[1].item
.a_int
- 1;
1569 free((genericptr_t
) selected
);
1570 vanq_sortmode
= choice
;
1572 return (n
< 0) ? -1 : vanq_sortmode
;
1575 /* #vanquished command */
1579 list_vanquished('a', FALSE
);
1583 /* high priests aren't unique but are flagged as such to simplify something */
1584 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1585 && mndx != PM_HIGH_PRIEST)
1588 list_vanquished(defquery
, ask
)
1594 unsigned ntypes
, ni
;
1595 long total_killed
= 0L;
1597 short mindx
[NUMMONS
];
1598 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1600 /* get totals first */
1602 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1603 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1605 mindx
[ntypes
++] = i
;
1606 total_killed
+= (long) nkilled
;
1609 /* vanquished creatures list;
1610 * includes all dead monsters, not just those killed by the player
1613 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1614 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1616 c
= ask
? yn_function(
1617 "Do you want an account of creatures vanquished?",
1618 ynaqchars
, defquery
)
1622 if (c
== 'y' || c
== 'a') {
1623 if (c
== 'a') { /* ask player to choose sort order */
1624 /* choose value for vanq_sortmode via menu; ESC cancels list
1625 of vanquished monsters but does not set 'done_stopprint' */
1626 if (set_vanq_order() < 0)
1629 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1630 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1631 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1633 klwin
= create_nhwindow(NHW_MENU
);
1634 putstr(klwin
, 0, "Vanquished creatures:");
1635 putstr(klwin
, 0, "");
1637 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1638 for (ni
= 0; ni
< ntypes
; ni
++) {
1640 nkilled
= mvitals
[i
].died
;
1641 mlet
= mons
[i
].mlet
;
1642 if (class_header
&& mlet
!= prev_mlet
) {
1643 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1644 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1648 if (UniqCritterIndx(i
)) {
1649 Sprintf(buf
, "%s%s",
1650 !type_is_pname(&mons
[i
]) ? "the " : "",
1655 Sprintf(eos(buf
), " (twice)");
1658 Sprintf(eos(buf
), " (thrice)");
1661 Sprintf(eos(buf
), " (%d times)", nkilled
);
1667 if (uniq_header
&& was_uniq
) {
1668 putstr(klwin
, 0, "");
1671 /* trolls or undead might have come back,
1672 but we don't keep track of that */
1674 Strcpy(buf
, an(mons
[i
].mname
));
1676 Sprintf(buf
, "%3d %s", nkilled
,
1677 makeplural(mons
[i
].mname
));
1679 /* number of leading spaces to match 3 digit prefix */
1680 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1681 : !strncmpi(buf
, "an ", 3) ? 1
1682 : !strncmpi(buf
, "a ", 2) ? 2
1683 : !digit(buf
[2]) ? 4 : 0;
1686 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1687 putstr(klwin
, 0, buftoo
);
1690 * if (Hallucination)
1691 * putstr(klwin, 0, "and a partridge in a pear tree");
1694 putstr(klwin
, 0, "");
1695 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1696 putstr(klwin
, 0, buf
);
1698 display_nhwindow(klwin
, TRUE
);
1699 destroy_nhwindow(klwin
);
1701 } else if (defquery
== 'a') {
1702 /* #dovanquished rather than final disclosure, so pline() is ok */
1703 pline("No monsters have been vanquished.");
1707 /* number of monster species which have been genocided */
1713 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1714 if (mvitals
[i
].mvflags
& G_GENOD
) {
1716 if (UniqCritterIndx(i
))
1717 impossible("unique creature '%d: %s' genocided?",
1729 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1730 if (UniqCritterIndx(i
))
1732 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1739 list_genocided(defquery
, ask
)
1744 int ngenocided
, nextinct
;
1749 ngenocided
= num_genocides();
1750 nextinct
= num_extinct();
1752 /* genocided or extinct species list */
1753 if (ngenocided
!= 0 || nextinct
!= 0) {
1754 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1755 (nextinct
&& !ngenocided
) ? "extinct " : "",
1756 (ngenocided
) ? " genocided" : "",
1757 (nextinct
&& ngenocided
) ? " and extinct" : "");
1758 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1762 klwin
= create_nhwindow(NHW_MENU
);
1763 Sprintf(buf
, "%s%s species:",
1764 (ngenocided
) ? "Genocided" : "Extinct",
1765 (nextinct
&& ngenocided
) ? " or extinct" : "");
1766 putstr(klwin
, 0, buf
);
1767 putstr(klwin
, 0, "");
1769 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1770 /* uniques can't be genocided but can become extinct;
1771 however, they're never reported as extinct, so skip them */
1772 if (UniqCritterIndx(i
))
1774 if (mvitals
[i
].mvflags
& G_GONE
) {
1775 Strcpy(buf
, makeplural(mons
[i
].mname
));
1777 * "Extinct" is unfortunate terminology. A species
1778 * is marked extinct when its birth limit is reached,
1779 * but there might be members of the species still
1780 * alive, contradicting the meaning of the word.
1782 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1783 Strcat(buf
, " (extinct)");
1784 putstr(klwin
, 0, buf
);
1787 putstr(klwin
, 0, "");
1788 if (ngenocided
> 0) {
1789 Sprintf(buf
, "%d species genocided.", ngenocided
);
1790 putstr(klwin
, 0, buf
);
1793 Sprintf(buf
, "%d species extinct.", nextinct
);
1794 putstr(klwin
, 0, buf
);
1797 display_nhwindow(klwin
, TRUE
);
1798 destroy_nhwindow(klwin
);
1803 /* set a delayed killer, ensure non-delayed killer is cleared out */
1805 delayed_killer(id
, format
, killername
)
1808 const char *killername
;
1810 struct kinfo
*k
= find_delayed_killer(id
);
1812 if (k
== (struct kinfo
*) 0) {
1813 /* no match, add a new delayed killer to the list */
1814 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1816 k
->next
= killer
.next
;
1821 Strcpy(k
->name
, killername
? killername
: "");
1826 find_delayed_killer(id
)
1831 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1839 dealloc_killer(kptr
)
1842 struct kinfo
*prev
= &killer
, *k
;
1844 if (kptr
== (struct kinfo
*) 0)
1846 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1852 if (k
== (struct kinfo
*) 0) {
1853 impossible("dealloc_killer not on list");
1855 prev
->next
= k
->next
;
1856 free((genericptr_t
) k
);
1861 save_killers(fd
, mode
)
1867 if (perform_bwrite(mode
)) {
1868 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1869 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1872 if (release_data(mode
)) {
1873 while (killer
.next
) {
1874 kptr
= killer
.next
->next
;
1875 free((genericptr_t
) killer
.next
);
1887 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1888 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1890 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1902 while (*p
&& isspace((uchar
) *p
))
1906 while (*p
&& !isspace((uchar
) *p
))
1918 out
+= strlen(out
); /* eos() */
1919 while (*in
&& isspace((uchar
) *in
))
1921 while (*in
&& !isspace((uchar
) *in
))
1928 build_english_list(in
)
1932 int len
= (int) strlen(p
), words
= wordcount(p
);
1934 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
1936 len
+= 3 + (words
- 1);
1937 out
= (char *) alloc(len
+ 1);
1938 *out
= '\0'; /* bel_copy1() appends */
1942 impossible("no words in list");
1950 /* "first or second" */
1954 /* "first, second, or third */
1958 } while (--words
> 1);