1 /* NetHack 3.6 end.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.127 $ */
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 *));
60 STATIC_DCL
void NDECL(dump_plines
);
62 STATIC_DCL
void FDECL(dump_everything
, (int));
63 STATIC_DCL
int NDECL(num_extinct
);
65 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
66 extern void FDECL(nethack_exit
, (int));
68 #define nethack_exit exit
71 #define done_stopprint program_state.stopprint
74 #define NH_abort NH_abort_
78 #define NH_abort_() Abort(0)
81 #define NH_abort_() (void) abort()
84 #define NH_abort_() win32_abort()
86 #define NH_abort_() abort()
93 #ifdef PANICTRACE_LIBC
97 /* What do we try and in what order? Tradeoffs:
98 * libc: +no external programs required
99 * -requires newish libc/glibc
100 * -requires -rdynamic
101 * gdb: +gives more detailed information
102 * +works on more OS versions
103 * -requires -g, which may preclude -O on some compilers
106 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
107 #ifdef PANICTRACE_LIBC
108 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
110 #define SYSOPT_PANICTRACE_LIBC 0
113 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
114 #ifdef PANICTRACE_LIBC
115 #define SYSOPT_PANICTRACE_LIBC 1
117 #define SYSOPT_PANICTRACE_LIBC 0
121 static void NDECL(NH_abort
);
123 static void FDECL(panictrace_handler
, (int));
125 static boolean
NDECL(NH_panictrace_libc
);
126 static boolean
NDECL(NH_panictrace_gdb
);
130 void panictrace_handler(
131 sig_unused
) /* called as signal() handler, so sent at least one arg */
132 int sig_unused UNUSED
;
134 #define SIG_MSG "\nSignal received.\n"
135 (void) write(2, SIG_MSG
, sizeof(SIG_MSG
) - 1);
140 panictrace_setsignals(set
)
143 #define SETSIGNAL(sig) \
144 (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
164 SETSIGNAL(SIGSTKFLT
);
174 #endif /* NO_SIGNAL */
179 int gdb_prio
= SYSOPT_PANICTRACE_GDB
;
180 int libc_prio
= SYSOPT_PANICTRACE_LIBC
;
181 static boolean aborting
= FALSE
;
188 if (gdb_prio
== libc_prio
&& gdb_prio
> 0)
191 if (gdb_prio
> libc_prio
) {
192 (void) (NH_panictrace_gdb() || (libc_prio
&& NH_panictrace_libc()));
194 (void) (NH_panictrace_libc() || (gdb_prio
&& NH_panictrace_gdb()));
198 /* overload otherwise unused priority for debug mode: 1 = show
199 traceback and exit; 2 = show traceback and stay in debugger */
200 /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
201 vms_traceback(gdb_prio
);
202 (void) libc_prio
; /* half-hearted attempt at lint suppression */
207 panictrace_setsignals(FALSE
);
215 #ifdef PANICTRACE_LIBC
220 raw_print("Generating more information you may report:\n");
221 count
= backtrace(bt
, SIZE(bt
));
222 info
= backtrace_symbols(bt
, count
);
223 for (x
= 0; x
< count
; x
++) {
224 raw_printf("[%lu] %s", (unsigned long) x
, info
[x
]);
226 /* free(info); -- Don't risk it. */
230 #endif /* !PANICTRACE_LIBC */
234 * fooPATH file system path for foo
235 * fooVAR (possibly const) variable containing fooPATH
237 #ifdef PANICTRACE_GDB
239 #define GDBVAR sysopt.gdbpath
240 #define GREPVAR sysopt.greppath
242 #define GDBVAR GDBPATH
243 #define GREPVAR GREPPATH
245 #endif /* PANICTRACE_GDB */
250 #ifdef PANICTRACE_GDB
251 /* A (more) generic method to get a stack trace - invoke
253 char *gdbpath
= GDBVAR
;
254 char *greppath
= GREPVAR
;
258 if (gdbpath
== NULL
|| gdbpath
[0] == 0)
260 if (greppath
== NULL
|| greppath
[0] == 0)
263 sprintf(buf
, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath
, ARGV0
, getpid(),
265 gdb
= popen(buf
, "w");
267 raw_print("Generating more information you may report:\n");
268 fprintf(gdb
, "bt\nquit\ny");
278 #endif /* !PANICTRACE_GDB */
280 #endif /* PANICTRACE */
283 * The order of these needs to match the macros in hack.h.
285 static NEARDATA
const char *deaths
[] = {
286 /* the array of death */
287 "died", "choked", "poisoned", "starvation", "drowning", "burning",
288 "dissolving under the heat and pressure", "crushed", "turned to stone",
289 "turned into slime", "genocided", "panic", "trickery", "quit",
290 "escaped", "ascended"
293 static NEARDATA
const char *ends
[] = {
295 "died", "choked", "were poisoned",
296 "starved", "drowned", "burned",
297 "dissolved in the lava",
298 "were crushed", "turned to stone",
299 "turned into slime", "were genocided",
300 "panicked", "were tricked", "quit",
301 "escaped", "ascended"
304 static boolean Schroedingers_cat
= FALSE
;
308 done1(sig_unused
) /* called as signal() handler, so sent at least one arg */
309 int sig_unused UNUSED
;
312 (void) signal(SIGINT
, SIG_IGN
);
316 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
318 clear_nhwindow(WIN_MESSAGE
);
328 /* "#quit" command or keyboard interrupt */
332 if (!paranoid_query(ParanoidQuit
, "Really quit?")) {
334 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
336 clear_nhwindow(WIN_MESSAGE
);
342 u
.uinvulnerable
= FALSE
; /* avoid ctrl-C bug -dlc */
347 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
351 extern int debuggable
; /* sys/vms/vmsmisc.c, vmsunix.c */
353 c
= !debuggable
? 'n' : ynq("Enter debugger?");
356 c
= ynq("Create SnapShot?");
358 c
= ynq("Dump core?");
363 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
365 exit_nhwindows((char *) 0);
380 done_intr(sig_unused
) /* called as signal() handler, so sent at least 1 arg */
381 int sig_unused UNUSED
;
384 (void) signal(SIGINT
, SIG_IGN
);
385 #if defined(UNIX) || defined(VMS)
386 (void) signal(SIGQUIT
, SIG_IGN
);
391 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
392 /* signal() handler */
397 program_state
.done_hup
++;
398 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
403 #endif /* NO_SIGNAL */
406 done_in_by(mtmp
, how
)
411 struct permonst
*mptr
= mtmp
->data
,
412 *champtr
= ((mtmp
->cham
>= LOW_PM
)
415 boolean distorted
= (boolean
) (Hallucination
&& canspotmon(mtmp
)),
416 mimicker
= (mtmp
->m_ap_type
== M_AP_MONSTER
),
417 imitator
= (mptr
!= champtr
|| mimicker
);
419 You((how
== STONING
) ? "turn to stone..." : "die...");
420 mark_synch(); /* flush buffered screen output */
422 killer
.format
= KILLED_BY_AN
;
423 /* "killed by the high priest of Crom" is okay,
424 "killed by the high priest" alone isn't */
425 if ((mptr
->geno
& G_UNIQ
) != 0 && !(imitator
&& !mimicker
)
426 && !(mptr
== &mons
[PM_HIGH_PRIEST
] && !mtmp
->ispriest
)) {
427 if (!type_is_pname(mptr
))
429 killer
.format
= KILLED_BY
;
431 /* _the_ <invisible> <distorted> ghost of Dudley */
432 if (mptr
== &mons
[PM_GHOST
] && has_mname(mtmp
)) {
434 killer
.format
= KILLED_BY
;
437 Strcat(buf
, "invisible ");
439 Strcat(buf
, "hallucinogen-distorted ");
443 const char *realnm
= champtr
->mname
, *fakenm
= mptr
->mname
;
444 boolean alt
= is_vampshifter(mtmp
);
447 /* realnm is already correct because champtr==mptr;
448 set up fake mptr for type_is_pname/the_unique_pm */
449 mptr
= &mons
[mtmp
->mappearance
];
450 fakenm
= mptr
->mname
;
451 } else if (alt
&& strstri(realnm
, "vampire")
452 && !strcmp(fakenm
, "vampire bat")) {
453 /* special case: use "vampire in bat form" in preference
454 to redundant looking "vampire in vampire bat form" */
457 /* for the alternate format, always suppress any article;
458 pname and the_unique should also have s_suffix() applied,
459 but vampires don't take on any shapes which warrant that */
460 if (alt
|| type_is_pname(mptr
)) /* no article */
461 Strcpy(shape
, fakenm
);
462 else if (the_unique_pm(mptr
)) /* "the"; don't use the() here */
463 Sprintf(shape
, "the %s", fakenm
);
465 Strcpy(shape
, an(fakenm
));
466 /* omit "called" to avoid excessive verbosity */
468 alt
? "%s in %s form"
469 : mimicker
? "%s disguised as %s"
472 mptr
= mtmp
->data
; /* reset for mimicker case */
473 } else if (mptr
== &mons
[PM_GHOST
]) {
474 Strcat(buf
, "ghost");
476 Sprintf(eos(buf
), " of %s", MNAME(mtmp
));
477 } else if (mtmp
->isshk
) {
478 const char *shknm
= shkname(mtmp
),
479 *honorific
= shkname_is_pname(mtmp
) ? ""
480 : mtmp
->female
? "Ms. " : "Mr. ";
482 Sprintf(eos(buf
), "%s%s, the shopkeeper", honorific
, shknm
);
483 killer
.format
= KILLED_BY
;
484 } else if (mtmp
->ispriest
|| mtmp
->isminion
) {
485 /* m_monnam() suppresses "the" prefix plus "invisible", and
486 it overrides the effect of Hallucination on priestname() */
487 Strcat(buf
, m_monnam(mtmp
));
489 Strcat(buf
, mptr
->mname
);
491 Sprintf(eos(buf
), " called %s", MNAME(mtmp
));
494 Strcpy(killer
.name
, buf
);
495 if (mptr
->mlet
== S_WRAITH
)
496 u
.ugrave_arise
= PM_WRAITH
;
497 else if (mptr
->mlet
== S_MUMMY
&& urace
.mummynum
!= NON_PM
)
498 u
.ugrave_arise
= urace
.mummynum
;
499 else if (mptr
->mlet
== S_VAMPIRE
&& Race_if(PM_HUMAN
))
500 u
.ugrave_arise
= PM_VAMPIRE
;
501 else if (mptr
== &mons
[PM_GHOUL
])
502 u
.ugrave_arise
= PM_GHOUL
;
503 /* this could happen if a high-end vampire kills the hero
504 when ordinary vampires are genocided; ditto for wraiths */
505 if (u
.ugrave_arise
>= LOW_PM
506 && (mvitals
[u
.ugrave_arise
].mvflags
& G_GENOD
))
507 u
.ugrave_arise
= NON_PM
;
513 /* some special cases for overriding while-helpless reason */
514 static const struct {
516 const char *exclude
, *include
;
518 /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
519 prevented any last-second recovery, but it was not the cause of
520 "petrified by <foo>" */
521 { STONING
, 1, "getting stoned", (char *) 0 },
522 /* "died of starvation, while fainted from lack of food" is accurate
523 but sounds a fairly silly (and doesn't actually appear unless you
524 splice together death and while-helpless from xlogfile) */
525 { STARVING
, 0, "fainted from lack of food", "fainted" },
528 /* clear away while-helpless when the cause of death caused that
529 helplessness (ie, "petrified by <foo> while getting stoned") */
537 for (i
= 0; i
< SIZE(death_fixups
); ++i
)
538 if (death_fixups
[i
].why
== how
539 && !strcmp(death_fixups
[i
].exclude
, multi_reason
)) {
540 if (death_fixups
[i
].include
) /* substitute alternate reason */
541 multi_reason
= death_fixups
[i
].include
;
542 else /* remove the helplessness reason */
543 multi_reason
= (char *) 0;
544 if (death_fixups
[i
].unmulti
) /* possibly hide helplessness */
551 #if defined(WIN32) && !defined(SYSCF)
552 #define NOTIFY_NETHACK_BUGS
557 VA_DECL(const char *, str
)
560 VA_INIT(str
, char *);
562 if (program_state
.panicking
++)
563 NH_abort(); /* avoid loops - this should never happen*/
565 if (iflags
.window_inited
) {
566 raw_print("\r\nOops...");
567 wait_synch(); /* make sure all pending output gets flushed */
568 exit_nhwindows((char *) 0);
569 iflags
.window_inited
= 0; /* they're gone; force raw_print()ing */
572 raw_print(program_state
.gameover
573 ? "Postgame wrapup disrupted."
574 : !program_state
.something_worth_saving
575 ? "Program initialization has failed."
576 : "Suddenly, the dungeon collapses.");
578 #if defined(NOTIFY_NETHACK_BUGS)
580 raw_printf("Report the following error to \"%s\" or at \"%s\".",
581 DEVTEAM_EMAIL
, DEVTEAM_URL
);
582 else if (program_state
.something_worth_saving
)
583 raw_print("\nError save file being written.\n");
586 const char *maybe_rebuild
= !program_state
.something_worth_saving
588 : "\nand it may be possible to rebuild.";
591 raw_printf("To report this error, %s%s", sysopt
.support
,
593 else if (sysopt
.fmtd_wizard_list
) /* formatted SYSCF WIZARDS */
594 raw_printf("To report this error, contact %s%s",
595 sysopt
.fmtd_wizard_list
, maybe_rebuild
);
597 raw_printf("Report error to \"%s\"%s", WIZARD_NAME
,
601 /* XXX can we move this above the prints? Then we'd be able to
602 * suppress "it may be possible to rebuild" based on dosave0()
603 * or say it's NOT possible to rebuild. */
604 if (program_state
.something_worth_saving
) {
605 set_error_savefile();
607 /* os/win port specific recover instructions */
609 raw_printf("%s", sysopt
.recover
);
616 Vsprintf(buf
, str
, VA_ARGS
);
618 paniclog("panic", buf
);
621 interject(INTERJECT_PANIC
);
623 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
625 NH_abort(); /* generate core dump */
628 really_done(PANICKED
);
632 should_query_disclose_option(category
, defquery
)
640 if ((dop
= index(disclosure_options
, category
)) != 0) {
641 idx
= (int) (dop
- disclosure_options
);
642 if (idx
< 0 || idx
>= NUM_DISCLOSURE_OPTIONS
) {
644 "should_query_disclose_option: bad disclosure index %d %c",
646 *defquery
= DISCLOSE_PROMPT_DEFAULT_YES
;
649 disclose
= flags
.end_disclose
[idx
];
650 if (disclose
== DISCLOSE_YES_WITHOUT_PROMPT
) {
653 } else if (disclose
== DISCLOSE_SPECIAL_WITHOUT_PROMPT
) {
656 } else if (disclose
== DISCLOSE_NO_WITHOUT_PROMPT
) {
659 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_YES
) {
662 } else if (disclose
== DISCLOSE_PROMPT_DEFAULT_SPECIAL
) {
670 impossible("should_query_disclose_option: bad category %c", category
);
679 char buf
[BUFSZ
], **strp
;
680 extern char *saved_plines
[];
681 extern unsigned saved_pline_index
;
685 putstr(0, 0, "Latest messages:");
686 for (i
= 0, j
= (int) saved_pline_index
; i
< DUMPLOG_MSG_COUNT
;
687 ++i
, j
= (j
+ 1) % DUMPLOG_MSG_COUNT
) {
688 strp
= &saved_plines
[j
];
690 copynchars(&buf
[1], *strp
, BUFSZ
- 1 - 1);
692 #ifdef FREE_ALL_MEMORY
693 free(*strp
), *strp
= 0;
709 if (!iflags
.in_dumplog
)
714 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
715 makeknown(obj
->otyp
);
716 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
717 if (Is_container(obj
) || obj
->otyp
== STATUE
)
718 obj
->cknown
= obj
->lknown
= 1;
721 Sprintf(pbuf
, "%s, %s %s %s %s", plname
,
722 aligns
[1 - u
.ualign
.type
].adj
,
723 genders
[flags
.female
].adj
,
725 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
730 putstr(0, 0, do_statusline1());
731 putstr(0, 0, do_statusline2());
736 putstr(0, 0, "Inventory:");
737 display_inventory((char *) 0, TRUE
);
738 container_contents(invent
, TRUE
, TRUE
, FALSE
);
739 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
740 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
743 list_vanquished('y', FALSE
);
745 list_genocided('a', FALSE
);
747 show_conduct((how
>= PANICKED
) ? 1 : 2);
749 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
751 dump_redirect(FALSE
);
762 char c
= '\0', defquery
;
766 if (invent
&& !done_stopprint
) {
768 Sprintf(qbuf
, "Do you want to see what you had when you %s?",
769 (how
== QUIT
) ? "quit" : "died");
771 Strcpy(qbuf
, "Do you want your possessions identified?");
773 ask
= should_query_disclose_option('i', &defquery
);
774 c
= ask
? yn_function(qbuf
, ynqchars
, defquery
) : defquery
;
778 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
779 makeknown(obj
->otyp
);
780 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
781 if (Is_container(obj
) || obj
->otyp
== STATUE
)
782 obj
->cknown
= obj
->lknown
= 1;
784 (void) display_inventory((char *) 0, TRUE
);
785 container_contents(invent
, TRUE
, TRUE
, FALSE
);
791 if (!done_stopprint
) {
792 ask
= should_query_disclose_option('a', &defquery
);
793 c
= ask
? yn_function("Do you want to see your attributes?", ynqchars
,
797 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
798 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
804 if (!done_stopprint
) {
805 ask
= should_query_disclose_option('v', &defquery
);
806 list_vanquished(defquery
, ask
);
809 if (!done_stopprint
) {
810 ask
= should_query_disclose_option('g', &defquery
);
811 list_genocided(defquery
, ask
);
814 if (!done_stopprint
) {
815 ask
= should_query_disclose_option('c', &defquery
);
816 c
= ask
? yn_function("Do you want to see your conduct?", ynqchars
,
820 show_conduct((how
>= PANICKED
) ? 1 : 2);
825 if (!done_stopprint
) {
826 ask
= should_query_disclose_option('o', &defquery
);
827 c
= ask
? yn_function("Do you want to see the dungeon overview?",
831 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
837 /* try to get the player back in a viable state after being killed */
842 int uhpmin
= max(2 * u
.ulevel
, 10);
844 if (u
.uhpmax
< uhpmin
)
848 if (u
.uhunger
< 500) {
852 /* cure impending doom of sickness hero won't have time to fix */
853 if ((Sick
& TIMEOUT
) == 1L) {
855 set_itimeout(&Sick
, 0L);
859 nomovemsg
= "You survived that attempt on your life.";
865 if (u
.utrap
&& u
.utraptype
== TT_LAVA
)
868 u
.ugrave_arise
= NON_PM
;
871 if (!context
.mon_moving
)
876 * Get valuables from the given list. Revised code: the list always remains
881 struct obj
*list
; /* inventory or container contents */
883 register struct obj
*obj
;
886 /* find amulets and gems, ignoring all artifacts */
887 for (obj
= list
; obj
; obj
= obj
->nobj
)
888 if (Has_contents(obj
)) {
889 get_valuables(obj
->cobj
);
890 } else if (obj
->oartifact
) {
892 } else if (obj
->oclass
== AMULET_CLASS
) {
893 i
= obj
->otyp
- FIRST_AMULET
;
894 if (!amulets
[i
].count
) {
895 amulets
[i
].count
= obj
->quan
;
896 amulets
[i
].typ
= obj
->otyp
;
898 amulets
[i
].count
+= obj
->quan
; /* always adds one */
899 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
900 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
901 if (!gems
[i
].count
) {
902 gems
[i
].count
= obj
->quan
;
903 gems
[i
].typ
= obj
->otyp
;
905 gems
[i
].count
+= obj
->quan
;
911 * Sort collected valuables, most frequent to least. We could just
912 * as easily use qsort, but we don't care about efficiency here.
915 sort_valuables(list
, size
)
916 struct valuable_data list
[];
917 int size
; /* max value is less than 20 */
920 struct valuable_data ltmp
;
922 /* move greater quantities to the front of the list */
923 for (i
= 1; i
< size
; i
++) {
924 if (list
[i
].count
== 0)
925 continue; /* empty slot */
926 ltmp
= list
[i
]; /* structure copy */
927 for (j
= i
; j
> 0; --j
)
928 if (list
[j
- 1].count
>= ltmp
.count
)
931 list
[j
] = list
[j
- 1];
941 odds_and_ends(list
, what
)
946 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
948 case CAT_CHECK
: /* Schroedinger's Cat */
949 /* Ascending is deterministic */
950 if (SchroedingersBox(otmp
))
954 if (Has_contents(otmp
))
955 return odds_and_ends(otmp
->cobj
, what
);
960 /* called twice; first to calculate total, then to list relevant items */
962 artifact_score(list
, counting
, endwin
)
964 boolean counting
; /* true => add up points; false => display them */
970 short dummy
; /* object type returned by artifact_name() */
972 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
973 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
974 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
975 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
976 value
= arti_cost(otmp
); /* zorkmid value */
977 points
= value
* 5 / 2; /* score value */
979 nowrap_add(u
.urexp
, points
);
981 makeknown(otmp
->otyp
);
982 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
983 /* assumes artifacts don't have quan > 1 */
984 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
985 the_unique_obj(otmp
) ? "The " : "",
986 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
987 : OBJ_NAME(objects
[otmp
->otyp
]),
988 value
, currency(value
), points
);
989 putstr(endwin
, 0, pbuf
);
992 if (Has_contents(otmp
))
993 artifact_score(otmp
->cobj
, counting
, endwin
);
997 /* Be careful not to call panic from here! */
1002 if (how
== TRICKED
) {
1003 if (killer
.name
[0]) {
1004 paniclog("trickery", killer
.name
);
1008 You("are a very tricky wizard, it seems.");
1012 if (program_state
.panicking
1013 #ifdef HANGUPHANDLING
1014 || program_state
.done_hup
1017 /* skip status update if panicking or disconnected */
1018 context
.botl
= context
.botlx
= FALSE
;
1020 /* otherwise force full status update */
1021 context
.botlx
= TRUE
;
1025 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
1026 killer
.format
= NO_KILLER_PREFIX
;
1027 /* Avoid killed by "a" burning or "a" starvation */
1028 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
1029 killer
.format
= KILLED_BY
;
1030 if (!killer
.name
[0] || how
>= PANICKED
)
1031 Strcpy(killer
.name
, deaths
[how
]);
1035 if (Lifesaved
&& (how
<= GENOCIDED
)) {
1036 pline("But wait...");
1037 makeknown(AMULET_OF_LIFE_SAVING
);
1038 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
1041 You_feel("much better!");
1042 pline_The("medallion crumbles to dust!");
1046 (void) adjattrib(A_CON
, -1, TRUE
);
1048 if (how
== GENOCIDED
) {
1049 pline("Unfortunately you are still genocided...");
1056 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
1057 && !paranoid_query(ParanoidDie
, "Die?")) {
1058 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
1067 /* separated from done() in order to specify the __noreturn__ attribute */
1074 winid endwin
= WIN_ERR
;
1075 boolean bones_ok
, have_windows
= iflags
.window_inited
;
1076 struct obj
*corpse
= (struct obj
*) 0;
1082 * The game is now over...
1084 program_state
.gameover
= 1;
1085 /* in case of a subsequent panic(), there's no point trying to save */
1086 program_state
.something_worth_saving
= 0;
1087 /* render vision subsystem inoperative */
1088 iflags
.vision_inited
= 0;
1090 /* might have been killed while using a disposable item, so make sure
1091 it's gone prior to inventory disclosure and creation of bones data */
1093 /* maybe not on object lists; if an active light source, would cause
1094 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1095 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
1096 dealloc_obj(thrownobj
);
1097 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
1098 dealloc_obj(kickedobj
);
1100 /* remember time of death here instead of having bones, rip, and
1101 topten figure it out separately and possibly getting different
1102 time or even day if player is slow responding to --More-- */
1103 urealtime
.finish_time
= endtime
= getnow();
1104 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1106 dump_open_log(endtime
);
1107 /* Sometimes you die on the first move. Life's not fair.
1108 * On those rare occasions you get hosed immediately, go out
1109 * smiling... :-) -3.
1111 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1112 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1115 wait_synch(); /* flush screen output */
1117 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1118 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1119 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1120 sethanguphandler(done_hangup
);
1122 #endif /* NO_SIGNAL */
1124 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1126 if (bones_ok
&& launch_in_progress())
1127 force_launch_placement();
1129 /* maintain ugrave_arise even for !bones_ok */
1130 if (how
== PANICKED
)
1131 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1132 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1133 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1134 else if (how
== STONING
)
1135 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1136 else if (how
== TURNED_SLIME
)
1137 u
.ugrave_arise
= PM_GREEN_SLIME
;
1139 /* if pets will contribute to score, populate mydogs list now
1140 (bones creation isn't a factor, but pline() messaging is) */
1141 if (how
== ESCAPED
|| how
== ASCENDED
)
1145 killer
.format
= NO_KILLER_PREFIX
;
1148 u
.umortality
++; /* skipped above when how==QUIT */
1149 Strcpy(killer
.name
, "quit while already on Charon's boat");
1152 if (how
== ESCAPED
|| how
== PANICKED
)
1153 killer
.format
= NO_KILLER_PREFIX
;
1155 fixup_death(how
); /* actually, fixup multi_reason */
1157 if (how
!= PANICKED
) {
1158 /* these affect score and/or bones, but avoid them during panic */
1159 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1163 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1168 display_nhwindow(WIN_MESSAGE
, FALSE
);
1170 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1171 disclose(how
, taken
);
1173 dump_everything(how
);
1175 /* finish_paybill should be called after disclosure but before bones */
1176 if (bones_ok
&& taken
)
1179 /* grave creation should be after disclosure so it doesn't have
1180 this grave in the current level's features for #overview */
1181 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1182 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1183 int mnum
= u
.umonnum
;
1186 /* Base corpse on race when not poly'd since original
1187 * u.umonnum is based on role, and all role monsters
1190 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1194 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1195 Sprintf(pbuf
, "%s, ", plname
);
1196 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1197 make_grave(u
.ux
, u
.uy
, pbuf
);
1199 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1201 /* calculate score, before creating bones [container gold] */
1203 int deepest
= deepest_lev_reached(FALSE
);
1205 umoney
= money_cnt(invent
);
1207 umoney
+= hidden_gold(); /* accumulate gold from containers */
1208 tmp
= umoney
- tmp
; /* net gain */
1214 tmp
+= 50L * (long) (deepest
- 1);
1216 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1217 nowrap_add(u
.urexp
, tmp
);
1219 /* ascension gives a score bonus iff offering to original deity */
1220 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1221 /* retaining original alignment: score *= 2;
1222 converting, then using helm-of-OA to switch back: *= 1.5 */
1223 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1226 nowrap_add(u
.urexp
, tmp
);
1230 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1231 /* give this feedback even if bones aren't going to be created,
1232 so that its presence or absence doesn't tip off the player to
1233 new bones or their lack; it might be a lie if makemon fails */
1234 Your("body rises from the dead as %s...",
1235 an(mons
[u
.ugrave_arise
].mname
));
1236 display_nhwindow(WIN_MESSAGE
, FALSE
);
1240 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1241 savebones(how
, endtime
, corpse
);
1242 /* corpse may be invalid pointer now so
1243 ensure that it isn't used again */
1244 corpse
= (struct obj
*) 0;
1247 /* update gold for the rip output, which can't use hidden_gold()
1248 (containers will be gone by then if bones just got saved...) */
1249 done_money
= umoney
;
1251 /* clean up unneeded windows */
1254 free_pickinv_cache(); /* extra persistent window if perm_invent */
1255 if (WIN_INVEN
!= WIN_ERR
)
1256 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1257 display_nhwindow(WIN_MESSAGE
, TRUE
);
1258 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1259 #ifndef STATUS_VIA_WINDOWPORT
1260 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1262 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1264 if (!done_stopprint
|| flags
.tombstone
)
1265 endwin
= create_nhwindow(NHW_TEXT
);
1267 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1268 outrip(endwin
, how
, endtime
);
1270 done_stopprint
= 1; /* just avoid any more output */
1273 dump_redirect(TRUE
);
1274 genl_outrip(0, how
, endtime
);
1275 dump_redirect(FALSE
);
1277 if (u
.uhave
.amulet
) {
1278 Strcat(killer
.name
, " (with the Amulet)");
1279 } else if (how
== ESCAPED
) {
1280 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1281 Strcat(killer
.name
, " (in celestial disgrace)");
1282 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1283 Strcat(killer
.name
, " (with a fake Amulet)");
1284 /* don't bother counting to see whether it should be plural */
1287 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1289 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1292 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1293 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1294 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1296 if (how
== ESCAPED
|| how
== ASCENDED
) {
1299 register struct val_list
*val
;
1302 for (val
= valuables
; val
->list
; val
++)
1303 for (i
= 0; i
< val
->size
; i
++) {
1304 val
->list
[i
].count
= 0L;
1306 get_valuables(invent
);
1308 /* add points for collected valuables */
1309 for (val
= valuables
; val
->list
; val
++)
1310 for (i
= 0; i
< val
->size
; i
++)
1311 if (val
->list
[i
].count
!= 0L) {
1312 tmp
= val
->list
[i
].count
1313 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1314 nowrap_add(u
.urexp
, tmp
);
1317 /* count the points for artifacts */
1318 artifact_score(invent
, TRUE
, endwin
);
1320 dump_redirect(TRUE
);
1321 artifact_score(invent
, TRUE
, endwin
);
1322 dump_redirect(FALSE
);
1325 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1327 Strcpy(pbuf
, "You");
1328 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1329 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1330 if (Schroedingers_cat
) {
1331 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1333 nowrap_add(u
.urexp
, mhp
);
1334 Strcat(eos(pbuf
), " and Schroedinger's cat");
1338 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1340 nowrap_add(u
.urexp
, mtmp
->mhp
);
1343 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1348 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1349 how
== ASCENDED
? "went to your reward"
1350 : "escaped from the dungeon",
1351 u
.urexp
, plur(u
.urexp
));
1352 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1354 if (!done_stopprint
)
1355 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1357 dump_redirect(TRUE
);
1358 artifact_score(invent
, FALSE
, 0);
1359 dump_redirect(FALSE
);
1362 /* list valuables here */
1363 for (val
= valuables
; val
->list
; val
++) {
1364 sort_valuables(val
->list
, val
->size
);
1365 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1366 int typ
= val
->list
[i
].typ
;
1367 long count
= val
->list
[i
].count
;
1371 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1372 otmp
= mksobj(typ
, FALSE
, FALSE
);
1373 makeknown(otmp
->otyp
);
1374 otmp
->known
= 1; /* for fake amulets */
1375 otmp
->dknown
= 1; /* seen it (blindness fix) */
1376 if (has_oname(otmp
))
1379 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1380 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1382 obfree(otmp
, (struct obj
*) 0);
1384 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1385 count
, plur(count
));
1387 dump_forward_putstr(endwin
, 0, pbuf
, 0);
1392 /* did not escape or ascend */
1393 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1394 /* level teleported out of the dungeon; `how' is DIED,
1395 due to falling or to "arriving at heaven prematurely" */
1396 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1397 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1399 /* more conventional demise */
1400 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1402 if (Is_astralevel(&u
.uz
))
1403 where
= "The Astral Plane";
1404 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1405 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1406 Sprintf(eos(pbuf
), " on dungeon level %d",
1407 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1410 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1411 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1414 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1415 plur(umoney
), moves
, plur(moves
));
1416 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1418 "You were level %d with a maximum of %d hit point%s when you %s.",
1419 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1420 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1421 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1422 if (!done_stopprint
)
1423 display_nhwindow(endwin
, TRUE
);
1424 if (endwin
!= WIN_ERR
)
1425 destroy_nhwindow(endwin
);
1428 /* "So when I die, the first thing I will see in Heaven is a
1430 if (have_windows
&& !iflags
.toptenwin
)
1431 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1432 topten(how
, endtime
);
1434 exit_nhwindows((char *) 0);
1436 if (done_stopprint
) {
1440 terminate(EXIT_SUCCESS
);
1444 container_contents(list
, identified
, all_containers
, reportempty
)
1446 boolean identified
, all_containers
, reportempty
;
1448 register struct obj
*box
, *obj
;
1450 boolean cat
, deadcat
;
1452 for (box
= list
; box
; box
= box
->nobj
) {
1453 if (Is_container(box
) || box
->otyp
== STATUE
) {
1454 box
->cknown
= 1; /* we're looking at the contents now */
1457 cat
= deadcat
= FALSE
;
1458 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1459 /* Schroedinger's Cat? */
1460 cat
= odds_and_ends(box
, CAT_CHECK
);
1462 Schroedingers_cat
= TRUE
;
1467 if (box
->otyp
== BAG_OF_TRICKS
) {
1468 continue; /* wrong type of container */
1469 } else if (box
->cobj
) {
1470 winid tmpwin
= create_nhwindow(NHW_MENU
);
1472 sortloot(&box
->cobj
,
1473 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1474 ? SORTLOOT_LOOT
: 0)
1475 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1477 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1478 putstr(tmpwin
, 0, buf
);
1479 putstr(tmpwin
, 0, "");
1480 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1482 makeknown(obj
->otyp
);
1483 obj
->known
= obj
->bknown
= obj
->dknown
1485 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1486 obj
->cknown
= obj
->lknown
= 1;
1488 putstr(tmpwin
, 0, doname(obj
));
1491 putstr(tmpwin
, 0, "Schroedinger's cat");
1493 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1494 display_nhwindow(tmpwin
, TRUE
);
1495 destroy_nhwindow(tmpwin
);
1497 container_contents(box
->cobj
, identified
, TRUE
,
1499 } else if (cat
|| deadcat
) {
1500 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1501 deadcat
? "dead " : "");
1502 display_nhwindow(WIN_MESSAGE
, FALSE
);
1503 } else if (reportempty
) {
1504 pline("%s is empty.", upstart(thesimpleoname(box
)));
1505 display_nhwindow(WIN_MESSAGE
, FALSE
);
1508 if (!all_containers
)
1513 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1518 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1520 getreturn("to exit");
1522 /* don't bother to try to release memory if we're in panic mode, to
1523 avoid trouble in case that happens to be due to memory problems */
1524 if (!program_state
.panicking
) {
1531 * This is liable to draw a warning if compiled with gcc, but it's
1532 * more important to flag panic() -> really_done() -> terminate()
1533 * as __noreturn__ then to avoid the warning.
1535 /* don't call exit() if already executing within an exit handler;
1536 that would cancel any other pending user-mode handlers */
1537 if (program_state
.exiting
)
1540 program_state
.exiting
= 1;
1541 nethack_exit(status
);
1544 extern const int monstr
[];
1546 static const char *vanqorders
[] = {
1547 "traditional: by monster level, by internal monster index",
1548 #define VANQ_MLVL_MNDX 0
1549 "by monster toughness, by internal monster index",
1550 #define VANQ_MSTR_MNDX 1
1551 "alphabetically, first unique monsters, then others",
1552 #define VANQ_ALPHA_SEP 2
1553 "alphabetically, unique monsters and others intermixed",
1554 #define VANQ_ALPHA_MIX 3
1555 "by monster class, high to low level within class",
1556 #define VANQ_MCLS_HTOL 4
1557 "by monster class, low to high level within class",
1558 #define VANQ_MCLS_LTOH 5
1559 "by count, high to low, by internal index within tied count",
1560 #define VANQ_COUNT_H_L 6
1561 "by count, low to high, by internal index within tied count",
1562 #define VANQ_COUNT_L_H 7
1564 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1566 STATIC_PTR
int CFDECLSPEC
1567 vanqsort_cmp(vptr1
, vptr2
)
1568 const genericptr vptr1
;
1569 const genericptr vptr2
;
1571 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1572 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1573 const char *name1
, *name2
, *punct
;
1576 switch (vanq_sortmode
) {
1578 case VANQ_MLVL_MNDX
:
1579 /* sort by monster level */
1580 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1581 res
= mlev2
- mlev1
; /* mlevel high to low */
1583 case VANQ_MSTR_MNDX
:
1584 /* sort by monster toughness */
1585 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1586 res
= mstr2
- mstr1
; /* monstr high to low */
1588 case VANQ_ALPHA_SEP
:
1589 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1590 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1591 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1592 res
= uniq2
- uniq1
;
1594 } /* else both unique or neither unique */
1596 case VANQ_ALPHA_MIX
:
1597 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1598 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1600 case VANQ_MCLS_HTOL
:
1601 case VANQ_MCLS_LTOH
:
1602 /* mons[].mlet is a small integer, 1..N, of type plain char;
1603 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1604 an inappropriate result when mlet2 is greater than mlet1,
1605 so force our copies (mcls1, mcls2) to be signed */
1606 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1607 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1608 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1609 punctuation characters are used for classes beyond those */
1610 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1611 /* force a specific order to the punctuation classes that's
1612 different from the internal order;
1613 internal order is ok if neither or just one is punctuation
1614 since letters have lower values so come out before punct */
1615 static const char punctclasses
[] = {
1616 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1619 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1620 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1621 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1622 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1624 res
= mcls1
- mcls2
; /* class */
1626 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1627 res
= mlev1
- mlev2
; /* mlevel low to high */
1628 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1629 res
= -res
; /* mlevel high to low */
1632 case VANQ_COUNT_H_L
:
1633 case VANQ_COUNT_L_H
:
1634 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1635 res
= died2
- died1
; /* dead count high to low */
1636 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1637 res
= -res
; /* dead count low to high */
1640 /* tiebreaker: internal mons[] index */
1642 res
= indx1
- indx2
; /* mndx low to high */
1646 /* returns -1 if cancelled via ESC */
1651 menu_item
*selected
;
1655 tmpwin
= create_nhwindow(NHW_MENU
);
1657 any
= zeroany
; /* zero out all bits */
1658 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1659 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1662 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1663 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1665 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1667 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1668 destroy_nhwindow(tmpwin
);
1670 choice
= selected
[0].item
.a_int
- 1;
1671 /* skip preselected entry if we have more than one item chosen */
1672 if (n
> 1 && choice
== vanq_sortmode
)
1673 choice
= selected
[1].item
.a_int
- 1;
1674 free((genericptr_t
) selected
);
1675 vanq_sortmode
= choice
;
1677 return (n
< 0) ? -1 : vanq_sortmode
;
1680 /* #vanquished command */
1684 list_vanquished('a', FALSE
);
1688 /* high priests aren't unique but are flagged as such to simplify something */
1689 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1690 && mndx != PM_HIGH_PRIEST)
1693 list_vanquished(defquery
, ask
)
1699 unsigned ntypes
, ni
;
1700 long total_killed
= 0L;
1702 short mindx
[NUMMONS
];
1703 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1705 /* get totals first */
1707 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1708 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1710 mindx
[ntypes
++] = i
;
1711 total_killed
+= (long) nkilled
;
1714 /* vanquished creatures list;
1715 * includes all dead monsters, not just those killed by the player
1718 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1719 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1721 c
= ask
? yn_function(
1722 "Do you want an account of creatures vanquished?",
1723 ynaqchars
, defquery
)
1727 if (c
== 'y' || c
== 'a') {
1728 if (c
== 'a') { /* ask player to choose sort order */
1729 /* choose value for vanq_sortmode via menu; ESC cancels list
1730 of vanquished monsters but does not set 'done_stopprint' */
1731 if (set_vanq_order() < 0)
1734 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1735 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1736 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1738 klwin
= create_nhwindow(NHW_MENU
);
1739 putstr(klwin
, 0, "Vanquished creatures:");
1740 putstr(klwin
, 0, "");
1742 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1743 for (ni
= 0; ni
< ntypes
; ni
++) {
1745 nkilled
= mvitals
[i
].died
;
1746 mlet
= mons
[i
].mlet
;
1747 if (class_header
&& mlet
!= prev_mlet
) {
1748 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1749 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1753 if (UniqCritterIndx(i
)) {
1754 Sprintf(buf
, "%s%s",
1755 !type_is_pname(&mons
[i
]) ? "the " : "",
1760 Sprintf(eos(buf
), " (twice)");
1763 Sprintf(eos(buf
), " (thrice)");
1766 Sprintf(eos(buf
), " (%d times)", nkilled
);
1772 if (uniq_header
&& was_uniq
) {
1773 putstr(klwin
, 0, "");
1776 /* trolls or undead might have come back,
1777 but we don't keep track of that */
1779 Strcpy(buf
, an(mons
[i
].mname
));
1781 Sprintf(buf
, "%3d %s", nkilled
,
1782 makeplural(mons
[i
].mname
));
1784 /* number of leading spaces to match 3 digit prefix */
1785 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1786 : !strncmpi(buf
, "an ", 3) ? 1
1787 : !strncmpi(buf
, "a ", 2) ? 2
1788 : !digit(buf
[2]) ? 4 : 0;
1791 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1792 putstr(klwin
, 0, buftoo
);
1795 * if (Hallucination)
1796 * putstr(klwin, 0, "and a partridge in a pear tree");
1799 putstr(klwin
, 0, "");
1800 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1801 putstr(klwin
, 0, buf
);
1803 display_nhwindow(klwin
, TRUE
);
1804 destroy_nhwindow(klwin
);
1806 } else if (defquery
== 'a') {
1807 /* #dovanquished rather than final disclosure, so pline() is ok */
1808 pline("No monsters have been vanquished.");
1812 /* number of monster species which have been genocided */
1818 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1819 if (mvitals
[i
].mvflags
& G_GENOD
) {
1821 if (UniqCritterIndx(i
))
1822 impossible("unique creature '%d: %s' genocided?",
1834 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1835 if (UniqCritterIndx(i
))
1837 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1844 list_genocided(defquery
, ask
)
1849 int ngenocided
, nextinct
;
1854 ngenocided
= num_genocides();
1855 nextinct
= num_extinct();
1857 /* genocided or extinct species list */
1858 if (ngenocided
!= 0 || nextinct
!= 0) {
1859 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1860 (nextinct
&& !ngenocided
) ? "extinct " : "",
1861 (ngenocided
) ? " genocided" : "",
1862 (nextinct
&& ngenocided
) ? " and extinct" : "");
1863 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1867 klwin
= create_nhwindow(NHW_MENU
);
1868 Sprintf(buf
, "%s%s species:",
1869 (ngenocided
) ? "Genocided" : "Extinct",
1870 (nextinct
&& ngenocided
) ? " or extinct" : "");
1871 putstr(klwin
, 0, buf
);
1872 putstr(klwin
, 0, "");
1874 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1875 /* uniques can't be genocided but can become extinct;
1876 however, they're never reported as extinct, so skip them */
1877 if (UniqCritterIndx(i
))
1879 if (mvitals
[i
].mvflags
& G_GONE
) {
1880 Strcpy(buf
, makeplural(mons
[i
].mname
));
1882 * "Extinct" is unfortunate terminology. A species
1883 * is marked extinct when its birth limit is reached,
1884 * but there might be members of the species still
1885 * alive, contradicting the meaning of the word.
1887 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1888 Strcat(buf
, " (extinct)");
1889 putstr(klwin
, 0, buf
);
1892 putstr(klwin
, 0, "");
1893 if (ngenocided
> 0) {
1894 Sprintf(buf
, "%d species genocided.", ngenocided
);
1895 putstr(klwin
, 0, buf
);
1898 Sprintf(buf
, "%d species extinct.", nextinct
);
1899 putstr(klwin
, 0, buf
);
1902 display_nhwindow(klwin
, TRUE
);
1903 destroy_nhwindow(klwin
);
1908 /* set a delayed killer, ensure non-delayed killer is cleared out */
1910 delayed_killer(id
, format
, killername
)
1913 const char *killername
;
1915 struct kinfo
*k
= find_delayed_killer(id
);
1917 if (k
== (struct kinfo
*) 0) {
1918 /* no match, add a new delayed killer to the list */
1919 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1920 (void) memset((genericptr_t
)k
, 0, sizeof(struct kinfo
));
1922 k
->next
= killer
.next
;
1927 Strcpy(k
->name
, killername
? killername
: "");
1932 find_delayed_killer(id
)
1937 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1945 dealloc_killer(kptr
)
1948 struct kinfo
*prev
= &killer
, *k
;
1950 if (kptr
== (struct kinfo
*) 0)
1952 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1958 if (k
== (struct kinfo
*) 0) {
1959 impossible("dealloc_killer not on list");
1961 prev
->next
= k
->next
;
1962 free((genericptr_t
) k
);
1967 save_killers(fd
, mode
)
1973 if (perform_bwrite(mode
)) {
1974 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1975 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1978 if (release_data(mode
)) {
1979 while (killer
.next
) {
1980 kptr
= killer
.next
->next
;
1981 free((genericptr_t
) killer
.next
);
1993 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1994 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1996 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
2008 while (*p
&& isspace((uchar
) *p
))
2012 while (*p
&& !isspace((uchar
) *p
))
2024 out
+= strlen(out
); /* eos() */
2025 while (*in
&& isspace((uchar
) *in
))
2027 while (*in
&& !isspace((uchar
) *in
))
2034 build_english_list(in
)
2038 int len
= (int) strlen(p
), words
= wordcount(p
);
2040 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2042 len
+= 3 + (words
- 1);
2043 out
= (char *) alloc(len
+ 1);
2044 *out
= '\0'; /* bel_copy1() appends */
2048 impossible("no words in list");
2056 /* "first or second" */
2060 /* "first, second, or third */
2064 } while (--words
> 1);