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
);
676 extern char* saved_plines
[];
679 putstr(0, 0, "Latest messages:");
680 for (i
= 0; i
< DUMPLOG_MSG_COUNT
; ++i
)
682 str
= saved_plines
[DUMPLOG_MSG_COUNT
- 1 - i
];
685 Sprintf(buf
, " %s", str
);
688 #ifdef FREE_ALL_MEMORY
696 dump_everything(how
, taken
)
706 if (!iflags
.in_dumplog
)
711 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
712 makeknown(obj
->otyp
);
713 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
714 if (Is_container(obj
) || obj
->otyp
== STATUE
)
715 obj
->cknown
= obj
->lknown
= 1;
718 Sprintf(pbuf
, "%s, %s %s %s %s", plname
,
719 aligns
[1 - u
.ualign
.type
].adj
,
720 genders
[flags
.female
].adj
,
722 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
727 putstr(0, 0, do_statusline1());
728 putstr(0, 0, do_statusline2());
733 putstr(0, 0, "Inventory:");
734 display_inventory((char *) 0, TRUE
);
735 container_contents(invent
, TRUE
, TRUE
, FALSE
);
736 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
737 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
740 list_vanquished('y', FALSE
);
742 list_genocided('a', FALSE
);
744 show_conduct((how
>= PANICKED
) ? 1 : 2);
746 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
748 dump_redirect(FALSE
);
757 char c
= '\0', defquery
;
761 if (invent
&& !done_stopprint
) {
763 Sprintf(qbuf
, "Do you want to see what you had when you %s?",
764 (how
== QUIT
) ? "quit" : "died");
766 Strcpy(qbuf
, "Do you want your possessions identified?");
768 ask
= should_query_disclose_option('i', &defquery
);
769 c
= ask
? yn_function(qbuf
, ynqchars
, defquery
) : defquery
;
773 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
774 makeknown(obj
->otyp
);
775 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
776 if (Is_container(obj
) || obj
->otyp
== STATUE
)
777 obj
->cknown
= obj
->lknown
= 1;
779 (void) display_inventory((char *) 0, TRUE
);
780 container_contents(invent
, TRUE
, TRUE
, FALSE
);
786 if (!done_stopprint
) {
787 ask
= should_query_disclose_option('a', &defquery
);
788 c
= ask
? yn_function("Do you want to see your attributes?", ynqchars
,
792 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
793 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
799 if (!done_stopprint
) {
800 ask
= should_query_disclose_option('v', &defquery
);
801 list_vanquished(defquery
, ask
);
804 if (!done_stopprint
) {
805 ask
= should_query_disclose_option('g', &defquery
);
806 list_genocided(defquery
, ask
);
809 if (!done_stopprint
) {
810 ask
= should_query_disclose_option('c', &defquery
);
811 c
= ask
? yn_function("Do you want to see your conduct?", ynqchars
,
815 show_conduct((how
>= PANICKED
) ? 1 : 2);
820 if (!done_stopprint
) {
821 ask
= should_query_disclose_option('o', &defquery
);
822 c
= ask
? yn_function("Do you want to see the dungeon overview?",
826 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
832 /* try to get the player back in a viable state after being killed */
837 int uhpmin
= max(2 * u
.ulevel
, 10);
839 if (u
.uhpmax
< uhpmin
)
843 if (u
.uhunger
< 500) {
847 /* cure impending doom of sickness hero won't have time to fix */
848 if ((Sick
& TIMEOUT
) == 1L) {
850 set_itimeout(&Sick
, 0L);
854 nomovemsg
= "You survived that attempt on your life.";
860 if (u
.utrap
&& u
.utraptype
== TT_LAVA
)
863 u
.ugrave_arise
= NON_PM
;
866 if (!context
.mon_moving
)
871 * Get valuables from the given list. Revised code: the list always remains
876 struct obj
*list
; /* inventory or container contents */
878 register struct obj
*obj
;
881 /* find amulets and gems, ignoring all artifacts */
882 for (obj
= list
; obj
; obj
= obj
->nobj
)
883 if (Has_contents(obj
)) {
884 get_valuables(obj
->cobj
);
885 } else if (obj
->oartifact
) {
887 } else if (obj
->oclass
== AMULET_CLASS
) {
888 i
= obj
->otyp
- FIRST_AMULET
;
889 if (!amulets
[i
].count
) {
890 amulets
[i
].count
= obj
->quan
;
891 amulets
[i
].typ
= obj
->otyp
;
893 amulets
[i
].count
+= obj
->quan
; /* always adds one */
894 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
895 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
896 if (!gems
[i
].count
) {
897 gems
[i
].count
= obj
->quan
;
898 gems
[i
].typ
= obj
->otyp
;
900 gems
[i
].count
+= obj
->quan
;
906 * Sort collected valuables, most frequent to least. We could just
907 * as easily use qsort, but we don't care about efficiency here.
910 sort_valuables(list
, size
)
911 struct valuable_data list
[];
912 int size
; /* max value is less than 20 */
915 struct valuable_data ltmp
;
917 /* move greater quantities to the front of the list */
918 for (i
= 1; i
< size
; i
++) {
919 if (list
[i
].count
== 0)
920 continue; /* empty slot */
921 ltmp
= list
[i
]; /* structure copy */
922 for (j
= i
; j
> 0; --j
)
923 if (list
[j
- 1].count
>= ltmp
.count
)
926 list
[j
] = list
[j
- 1];
936 odds_and_ends(list
, what
)
941 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
943 case CAT_CHECK
: /* Schroedinger's Cat */
944 /* Ascending is deterministic */
945 if (SchroedingersBox(otmp
))
949 if (Has_contents(otmp
))
950 return odds_and_ends(otmp
->cobj
, what
);
955 /* called twice; first to calculate total, then to list relevant items */
957 artifact_score(list
, counting
, endwin
)
959 boolean counting
; /* true => add up points; false => display them */
965 short dummy
; /* object type returned by artifact_name() */
967 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
968 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
969 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
970 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
971 value
= arti_cost(otmp
); /* zorkmid value */
972 points
= value
* 5 / 2; /* score value */
974 nowrap_add(u
.urexp
, points
);
976 makeknown(otmp
->otyp
);
977 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
978 /* assumes artifacts don't have quan > 1 */
979 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
980 the_unique_obj(otmp
) ? "The " : "",
981 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
982 : OBJ_NAME(objects
[otmp
->otyp
]),
983 value
, currency(value
), points
);
984 putstr(endwin
, 0, pbuf
);
987 if (Has_contents(otmp
))
988 artifact_score(otmp
->cobj
, counting
, endwin
);
992 /* Be careful not to call panic from here! */
997 if (how
== TRICKED
) {
998 if (killer
.name
[0]) {
999 paniclog("trickery", killer
.name
);
1003 You("are a very tricky wizard, it seems.");
1007 if (program_state
.panicking
1008 #ifdef HANGUPHANDLING
1009 || program_state
.done_hup
1012 /* skip status update if panicking or disconnected */
1013 context
.botl
= context
.botlx
= FALSE
;
1015 /* otherwise force full status update */
1016 context
.botlx
= TRUE
;
1020 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
1021 killer
.format
= NO_KILLER_PREFIX
;
1022 /* Avoid killed by "a" burning or "a" starvation */
1023 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
1024 killer
.format
= KILLED_BY
;
1025 if (!killer
.name
[0] || how
>= PANICKED
)
1026 Strcpy(killer
.name
, deaths
[how
]);
1030 if (Lifesaved
&& (how
<= GENOCIDED
)) {
1031 pline("But wait...");
1032 makeknown(AMULET_OF_LIFE_SAVING
);
1033 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
1036 You_feel("much better!");
1037 pline_The("medallion crumbles to dust!");
1041 (void) adjattrib(A_CON
, -1, TRUE
);
1043 if (how
== GENOCIDED
) {
1044 pline("Unfortunately you are still genocided...");
1051 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
1052 && !paranoid_query(ParanoidDie
, "Die?")) {
1053 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
1062 /* separated from done() in order to specify the __noreturn__ attribute */
1069 winid endwin
= WIN_ERR
;
1070 boolean bones_ok
, have_windows
= iflags
.window_inited
;
1071 struct obj
*corpse
= (struct obj
*) 0;
1077 * The game is now over...
1079 program_state
.gameover
= 1;
1080 /* in case of a subsequent panic(), there's no point trying to save */
1081 program_state
.something_worth_saving
= 0;
1082 /* render vision subsystem inoperative */
1083 iflags
.vision_inited
= 0;
1085 /* might have been killed while using a disposable item, so make sure
1086 it's gone prior to inventory disclosure and creation of bones data */
1088 /* maybe not on object lists; if an active light source, would cause
1089 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1090 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
1091 dealloc_obj(thrownobj
);
1092 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
1093 dealloc_obj(kickedobj
);
1095 /* remember time of death here instead of having bones, rip, and
1096 topten figure it out separately and possibly getting different
1097 time or even day if player is slow responding to --More-- */
1098 urealtime
.finish_time
= endtime
= getnow();
1099 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1101 dump_open_log(endtime
);
1102 /* Sometimes you die on the first move. Life's not fair.
1103 * On those rare occasions you get hosed immediately, go out
1104 * smiling... :-) -3.
1106 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1107 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1110 wait_synch(); /* flush screen output */
1112 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1113 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1114 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1115 sethanguphandler(done_hangup
);
1117 #endif /* NO_SIGNAL */
1119 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1121 if (bones_ok
&& launch_in_progress())
1122 force_launch_placement();
1124 /* maintain ugrave_arise even for !bones_ok */
1125 if (how
== PANICKED
)
1126 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1127 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1128 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1129 else if (how
== STONING
)
1130 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1131 else if (how
== TURNED_SLIME
)
1132 u
.ugrave_arise
= PM_GREEN_SLIME
;
1134 /* if pets will contribute to score, populate mydogs list now
1135 (bones creation isn't a factor, but pline() messaging is) */
1136 if (how
== ESCAPED
|| how
== ASCENDED
)
1140 killer
.format
= NO_KILLER_PREFIX
;
1143 u
.umortality
++; /* skipped above when how==QUIT */
1144 Strcpy(killer
.name
, "quit while already on Charon's boat");
1147 if (how
== ESCAPED
|| how
== PANICKED
)
1148 killer
.format
= NO_KILLER_PREFIX
;
1150 fixup_death(how
); /* actually, fixup multi_reason */
1152 if (how
!= PANICKED
) {
1153 /* these affect score and/or bones, but avoid them during panic */
1154 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1158 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1163 display_nhwindow(WIN_MESSAGE
, FALSE
);
1165 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1166 disclose(how
, taken
);
1167 dump_everything(how
, taken
);
1169 /* finish_paybill should be called after disclosure but before bones */
1170 if (bones_ok
&& taken
)
1173 /* grave creation should be after disclosure so it doesn't have
1174 this grave in the current level's features for #overview */
1175 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1176 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1177 int mnum
= u
.umonnum
;
1180 /* Base corpse on race when not poly'd since original
1181 * u.umonnum is based on role, and all role monsters
1184 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1188 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1189 Sprintf(pbuf
, "%s, ", plname
);
1190 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1191 make_grave(u
.ux
, u
.uy
, pbuf
);
1193 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1195 /* calculate score, before creating bones [container gold] */
1197 int deepest
= deepest_lev_reached(FALSE
);
1199 umoney
= money_cnt(invent
);
1201 umoney
+= hidden_gold(); /* accumulate gold from containers */
1202 tmp
= umoney
- tmp
; /* net gain */
1208 tmp
+= 50L * (long) (deepest
- 1);
1210 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1211 nowrap_add(u
.urexp
, tmp
);
1213 /* ascension gives a score bonus iff offering to original deity */
1214 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1215 /* retaining original alignment: score *= 2;
1216 converting, then using helm-of-OA to switch back: *= 1.5 */
1217 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1220 nowrap_add(u
.urexp
, tmp
);
1224 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1225 /* give this feedback even if bones aren't going to be created,
1226 so that its presence or absence doesn't tip off the player to
1227 new bones or their lack; it might be a lie if makemon fails */
1228 Your("body rises from the dead as %s...",
1229 an(mons
[u
.ugrave_arise
].mname
));
1230 display_nhwindow(WIN_MESSAGE
, FALSE
);
1234 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1235 savebones(how
, endtime
, corpse
);
1236 /* corpse may be invalid pointer now so
1237 ensure that it isn't used again */
1238 corpse
= (struct obj
*) 0;
1241 /* update gold for the rip output, which can't use hidden_gold()
1242 (containers will be gone by then if bones just got saved...) */
1243 done_money
= umoney
;
1245 /* clean up unneeded windows */
1248 free_pickinv_cache(); /* extra persistent window if perm_invent */
1249 if (WIN_INVEN
!= WIN_ERR
)
1250 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1251 display_nhwindow(WIN_MESSAGE
, TRUE
);
1252 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1253 #ifndef STATUS_VIA_WINDOWPORT
1254 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1256 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1258 if (!done_stopprint
|| flags
.tombstone
)
1259 endwin
= create_nhwindow(NHW_TEXT
);
1261 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1262 outrip(endwin
, how
, endtime
);
1264 done_stopprint
= 1; /* just avoid any more output */
1267 dump_redirect(TRUE
);
1268 genl_outrip(0, how
, endtime
);
1269 dump_redirect(FALSE
);
1271 if (u
.uhave
.amulet
) {
1272 Strcat(killer
.name
, " (with the Amulet)");
1273 } else if (how
== ESCAPED
) {
1274 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1275 Strcat(killer
.name
, " (in celestial disgrace)");
1276 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1277 Strcat(killer
.name
, " (with a fake Amulet)");
1278 /* don't bother counting to see whether it should be plural */
1281 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1283 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1286 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1287 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1288 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1290 if (how
== ESCAPED
|| how
== ASCENDED
) {
1293 register struct val_list
*val
;
1296 for (val
= valuables
; val
->list
; val
++)
1297 for (i
= 0; i
< val
->size
; i
++) {
1298 val
->list
[i
].count
= 0L;
1300 get_valuables(invent
);
1302 /* add points for collected valuables */
1303 for (val
= valuables
; val
->list
; val
++)
1304 for (i
= 0; i
< val
->size
; i
++)
1305 if (val
->list
[i
].count
!= 0L) {
1306 tmp
= val
->list
[i
].count
1307 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1308 nowrap_add(u
.urexp
, tmp
);
1311 /* count the points for artifacts */
1312 artifact_score(invent
, TRUE
, endwin
);
1314 dump_redirect(TRUE
);
1315 artifact_score(invent
, TRUE
, endwin
);
1316 dump_redirect(FALSE
);
1319 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1321 Strcpy(pbuf
, "You");
1322 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1323 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1324 if (Schroedingers_cat
) {
1325 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1327 nowrap_add(u
.urexp
, mhp
);
1328 Strcat(eos(pbuf
), " and Schroedinger's cat");
1332 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1334 nowrap_add(u
.urexp
, mtmp
->mhp
);
1337 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1342 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1343 how
== ASCENDED
? "went to your reward"
1344 : "escaped from the dungeon",
1345 u
.urexp
, plur(u
.urexp
));
1346 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1348 if (!done_stopprint
)
1349 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1351 dump_redirect(TRUE
);
1352 artifact_score(invent
, FALSE
, 0);
1353 dump_redirect(FALSE
);
1356 /* list valuables here */
1357 for (val
= valuables
; val
->list
; val
++) {
1358 sort_valuables(val
->list
, val
->size
);
1359 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1360 int typ
= val
->list
[i
].typ
;
1361 long count
= val
->list
[i
].count
;
1365 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1366 otmp
= mksobj(typ
, FALSE
, FALSE
);
1367 makeknown(otmp
->otyp
);
1368 otmp
->known
= 1; /* for fake amulets */
1369 otmp
->dknown
= 1; /* seen it (blindness fix) */
1370 if (has_oname(otmp
))
1373 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1374 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1376 obfree(otmp
, (struct obj
*) 0);
1378 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1379 count
, plur(count
));
1381 dump_forward_putstr(endwin
, 0, pbuf
, 0);
1386 /* did not escape or ascend */
1387 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1388 /* level teleported out of the dungeon; `how' is DIED,
1389 due to falling or to "arriving at heaven prematurely" */
1390 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1391 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1393 /* more conventional demise */
1394 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1396 if (Is_astralevel(&u
.uz
))
1397 where
= "The Astral Plane";
1398 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1399 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1400 Sprintf(eos(pbuf
), " on dungeon level %d",
1401 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1404 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1405 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1408 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1409 plur(umoney
), moves
, plur(moves
));
1410 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1412 "You were level %d with a maximum of %d hit point%s when you %s.",
1413 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1414 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1415 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1416 if (!done_stopprint
)
1417 display_nhwindow(endwin
, TRUE
);
1418 if (endwin
!= WIN_ERR
)
1419 destroy_nhwindow(endwin
);
1422 /* "So when I die, the first thing I will see in Heaven is a
1424 if (have_windows
&& !iflags
.toptenwin
)
1425 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1426 topten(how
, endtime
);
1428 exit_nhwindows((char *) 0);
1430 if (done_stopprint
) {
1434 terminate(EXIT_SUCCESS
);
1438 container_contents(list
, identified
, all_containers
, reportempty
)
1440 boolean identified
, all_containers
, reportempty
;
1442 register struct obj
*box
, *obj
;
1444 boolean cat
, deadcat
;
1446 for (box
= list
; box
; box
= box
->nobj
) {
1447 if (Is_container(box
) || box
->otyp
== STATUE
) {
1448 box
->cknown
= 1; /* we're looking at the contents now */
1451 cat
= deadcat
= FALSE
;
1452 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1453 /* Schroedinger's Cat? */
1454 cat
= odds_and_ends(box
, CAT_CHECK
);
1456 Schroedingers_cat
= TRUE
;
1461 if (box
->otyp
== BAG_OF_TRICKS
) {
1462 continue; /* wrong type of container */
1463 } else if (box
->cobj
) {
1464 winid tmpwin
= create_nhwindow(NHW_MENU
);
1466 sortloot(&box
->cobj
,
1467 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1468 ? SORTLOOT_LOOT
: 0)
1469 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1471 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1472 putstr(tmpwin
, 0, buf
);
1473 putstr(tmpwin
, 0, "");
1474 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1476 makeknown(obj
->otyp
);
1477 obj
->known
= obj
->bknown
= obj
->dknown
1479 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1480 obj
->cknown
= obj
->lknown
= 1;
1482 putstr(tmpwin
, 0, doname(obj
));
1485 putstr(tmpwin
, 0, "Schroedinger's cat");
1487 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1488 display_nhwindow(tmpwin
, TRUE
);
1489 destroy_nhwindow(tmpwin
);
1491 container_contents(box
->cobj
, identified
, TRUE
,
1493 } else if (cat
|| deadcat
) {
1494 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1495 deadcat
? "dead " : "");
1496 display_nhwindow(WIN_MESSAGE
, FALSE
);
1497 } else if (reportempty
) {
1498 pline("%s is empty.", upstart(thesimpleoname(box
)));
1499 display_nhwindow(WIN_MESSAGE
, FALSE
);
1502 if (!all_containers
)
1507 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1512 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1514 getreturn("to exit");
1516 /* don't bother to try to release memory if we're in panic mode, to
1517 avoid trouble in case that happens to be due to memory problems */
1518 if (!program_state
.panicking
) {
1525 * This is liable to draw a warning if compiled with gcc, but it's
1526 * more important to flag panic() -> really_done() -> terminate()
1527 * as __noreturn__ then to avoid the warning.
1529 /* don't call exit() if already executing within an exit handler;
1530 that would cancel any other pending user-mode handlers */
1531 if (program_state
.exiting
)
1534 program_state
.exiting
= 1;
1535 nethack_exit(status
);
1538 extern const int monstr
[];
1540 static const char *vanqorders
[] = {
1541 "traditional: by monster level, by internal monster index",
1542 #define VANQ_MLVL_MNDX 0
1543 "by monster toughness, by internal monster index",
1544 #define VANQ_MSTR_MNDX 1
1545 "alphabetically, first unique monsters, then others",
1546 #define VANQ_ALPHA_SEP 2
1547 "alphabetically, unique monsters and others intermixed",
1548 #define VANQ_ALPHA_MIX 3
1549 "by monster class, high to low level within class",
1550 #define VANQ_MCLS_HTOL 4
1551 "by monster class, low to high level within class",
1552 #define VANQ_MCLS_LTOH 5
1553 "by count, high to low, by internal index within tied count",
1554 #define VANQ_COUNT_H_L 6
1555 "by count, low to high, by internal index within tied count",
1556 #define VANQ_COUNT_L_H 7
1558 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1560 STATIC_PTR
int CFDECLSPEC
1561 vanqsort_cmp(vptr1
, vptr2
)
1562 const genericptr vptr1
;
1563 const genericptr vptr2
;
1565 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1566 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1567 const char *name1
, *name2
, *punct
;
1570 switch (vanq_sortmode
) {
1572 case VANQ_MLVL_MNDX
:
1573 /* sort by monster level */
1574 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1575 res
= mlev2
- mlev1
; /* mlevel high to low */
1577 case VANQ_MSTR_MNDX
:
1578 /* sort by monster toughness */
1579 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1580 res
= mstr2
- mstr1
; /* monstr high to low */
1582 case VANQ_ALPHA_SEP
:
1583 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1584 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1585 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1586 res
= uniq2
- uniq1
;
1588 } /* else both unique or neither unique */
1590 case VANQ_ALPHA_MIX
:
1591 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1592 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1594 case VANQ_MCLS_HTOL
:
1595 case VANQ_MCLS_LTOH
:
1596 /* mons[].mlet is a small integer, 1..N, of type plain char;
1597 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1598 an inappropriate result when mlet2 is greater than mlet1,
1599 so force our copies (mcls1, mcls2) to be signed */
1600 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1601 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1602 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1603 punctuation characters are used for classes beyond those */
1604 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1605 /* force a specific order to the punctuation classes that's
1606 different from the internal order;
1607 internal order is ok if neither or just one is punctuation
1608 since letters have lower values so come out before punct */
1609 static const char punctclasses
[] = {
1610 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1613 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1614 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1615 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1616 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1618 res
= mcls1
- mcls2
; /* class */
1620 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1621 res
= mlev1
- mlev2
; /* mlevel low to high */
1622 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1623 res
= -res
; /* mlevel high to low */
1626 case VANQ_COUNT_H_L
:
1627 case VANQ_COUNT_L_H
:
1628 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1629 res
= died2
- died1
; /* dead count high to low */
1630 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1631 res
= -res
; /* dead count low to high */
1634 /* tiebreaker: internal mons[] index */
1636 res
= indx1
- indx2
; /* mndx low to high */
1640 /* returns -1 if cancelled via ESC */
1645 menu_item
*selected
;
1649 tmpwin
= create_nhwindow(NHW_MENU
);
1651 any
= zeroany
; /* zero out all bits */
1652 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1653 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1656 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1657 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1659 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1661 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1662 destroy_nhwindow(tmpwin
);
1664 choice
= selected
[0].item
.a_int
- 1;
1665 /* skip preselected entry if we have more than one item chosen */
1666 if (n
> 1 && choice
== vanq_sortmode
)
1667 choice
= selected
[1].item
.a_int
- 1;
1668 free((genericptr_t
) selected
);
1669 vanq_sortmode
= choice
;
1671 return (n
< 0) ? -1 : vanq_sortmode
;
1674 /* #vanquished command */
1678 list_vanquished('a', FALSE
);
1682 /* high priests aren't unique but are flagged as such to simplify something */
1683 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1684 && mndx != PM_HIGH_PRIEST)
1687 list_vanquished(defquery
, ask
)
1693 unsigned ntypes
, ni
;
1694 long total_killed
= 0L;
1696 short mindx
[NUMMONS
];
1697 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1699 /* get totals first */
1701 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1702 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1704 mindx
[ntypes
++] = i
;
1705 total_killed
+= (long) nkilled
;
1708 /* vanquished creatures list;
1709 * includes all dead monsters, not just those killed by the player
1712 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1713 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1715 c
= ask
? yn_function(
1716 "Do you want an account of creatures vanquished?",
1717 ynaqchars
, defquery
)
1721 if (c
== 'y' || c
== 'a') {
1722 if (c
== 'a') { /* ask player to choose sort order */
1723 /* choose value for vanq_sortmode via menu; ESC cancels list
1724 of vanquished monsters but does not set 'done_stopprint' */
1725 if (set_vanq_order() < 0)
1728 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1729 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1730 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1732 klwin
= create_nhwindow(NHW_MENU
);
1733 putstr(klwin
, 0, "Vanquished creatures:");
1734 putstr(klwin
, 0, "");
1736 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1737 for (ni
= 0; ni
< ntypes
; ni
++) {
1739 nkilled
= mvitals
[i
].died
;
1740 mlet
= mons
[i
].mlet
;
1741 if (class_header
&& mlet
!= prev_mlet
) {
1742 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1743 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1747 if (UniqCritterIndx(i
)) {
1748 Sprintf(buf
, "%s%s",
1749 !type_is_pname(&mons
[i
]) ? "the " : "",
1754 Sprintf(eos(buf
), " (twice)");
1757 Sprintf(eos(buf
), " (thrice)");
1760 Sprintf(eos(buf
), " (%d times)", nkilled
);
1766 if (uniq_header
&& was_uniq
) {
1767 putstr(klwin
, 0, "");
1770 /* trolls or undead might have come back,
1771 but we don't keep track of that */
1773 Strcpy(buf
, an(mons
[i
].mname
));
1775 Sprintf(buf
, "%3d %s", nkilled
,
1776 makeplural(mons
[i
].mname
));
1778 /* number of leading spaces to match 3 digit prefix */
1779 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1780 : !strncmpi(buf
, "an ", 3) ? 1
1781 : !strncmpi(buf
, "a ", 2) ? 2
1782 : !digit(buf
[2]) ? 4 : 0;
1785 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1786 putstr(klwin
, 0, buftoo
);
1789 * if (Hallucination)
1790 * putstr(klwin, 0, "and a partridge in a pear tree");
1793 putstr(klwin
, 0, "");
1794 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1795 putstr(klwin
, 0, buf
);
1797 display_nhwindow(klwin
, TRUE
);
1798 destroy_nhwindow(klwin
);
1800 } else if (defquery
== 'a') {
1801 /* #dovanquished rather than final disclosure, so pline() is ok */
1802 pline("No monsters have been vanquished.");
1806 /* number of monster species which have been genocided */
1812 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1813 if (mvitals
[i
].mvflags
& G_GENOD
) {
1815 if (UniqCritterIndx(i
))
1816 impossible("unique creature '%d: %s' genocided?",
1828 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1829 if (UniqCritterIndx(i
))
1831 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1838 list_genocided(defquery
, ask
)
1843 int ngenocided
, nextinct
;
1848 ngenocided
= num_genocides();
1849 nextinct
= num_extinct();
1851 /* genocided or extinct species list */
1852 if (ngenocided
!= 0 || nextinct
!= 0) {
1853 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1854 (nextinct
&& !ngenocided
) ? "extinct " : "",
1855 (ngenocided
) ? " genocided" : "",
1856 (nextinct
&& ngenocided
) ? " and extinct" : "");
1857 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1861 klwin
= create_nhwindow(NHW_MENU
);
1862 Sprintf(buf
, "%s%s species:",
1863 (ngenocided
) ? "Genocided" : "Extinct",
1864 (nextinct
&& ngenocided
) ? " or extinct" : "");
1865 putstr(klwin
, 0, buf
);
1866 putstr(klwin
, 0, "");
1868 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1869 /* uniques can't be genocided but can become extinct;
1870 however, they're never reported as extinct, so skip them */
1871 if (UniqCritterIndx(i
))
1873 if (mvitals
[i
].mvflags
& G_GONE
) {
1874 Strcpy(buf
, makeplural(mons
[i
].mname
));
1876 * "Extinct" is unfortunate terminology. A species
1877 * is marked extinct when its birth limit is reached,
1878 * but there might be members of the species still
1879 * alive, contradicting the meaning of the word.
1881 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1882 Strcat(buf
, " (extinct)");
1883 putstr(klwin
, 0, buf
);
1886 putstr(klwin
, 0, "");
1887 if (ngenocided
> 0) {
1888 Sprintf(buf
, "%d species genocided.", ngenocided
);
1889 putstr(klwin
, 0, buf
);
1892 Sprintf(buf
, "%d species extinct.", nextinct
);
1893 putstr(klwin
, 0, buf
);
1896 display_nhwindow(klwin
, TRUE
);
1897 destroy_nhwindow(klwin
);
1902 /* set a delayed killer, ensure non-delayed killer is cleared out */
1904 delayed_killer(id
, format
, killername
)
1907 const char *killername
;
1909 struct kinfo
*k
= find_delayed_killer(id
);
1911 if (k
== (struct kinfo
*) 0) {
1912 /* no match, add a new delayed killer to the list */
1913 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1914 (void) memset((genericptr_t
)k
, 0, sizeof(struct kinfo
));
1916 k
->next
= killer
.next
;
1921 Strcpy(k
->name
, killername
? killername
: "");
1926 find_delayed_killer(id
)
1931 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1939 dealloc_killer(kptr
)
1942 struct kinfo
*prev
= &killer
, *k
;
1944 if (kptr
== (struct kinfo
*) 0)
1946 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1952 if (k
== (struct kinfo
*) 0) {
1953 impossible("dealloc_killer not on list");
1955 prev
->next
= k
->next
;
1956 free((genericptr_t
) k
);
1961 save_killers(fd
, mode
)
1967 if (perform_bwrite(mode
)) {
1968 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1969 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1972 if (release_data(mode
)) {
1973 while (killer
.next
) {
1974 kptr
= killer
.next
->next
;
1975 free((genericptr_t
) killer
.next
);
1987 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1988 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1990 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
2002 while (*p
&& isspace((uchar
) *p
))
2006 while (*p
&& !isspace((uchar
) *p
))
2018 out
+= strlen(out
); /* eos() */
2019 while (*in
&& isspace((uchar
) *in
))
2021 while (*in
&& !isspace((uchar
) *in
))
2028 build_english_list(in
)
2032 int len
= (int) strlen(p
), words
= wordcount(p
);
2034 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2036 len
+= 3 + (words
- 1);
2037 out
= (char *) alloc(len
+ 1);
2038 *out
= '\0'; /* bel_copy1() appends */
2042 impossible("no words in list");
2050 /* "first or second" */
2054 /* "first, second, or third */
2058 } while (--words
> 1);