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
[];
684 putstr(0, 0, "Latest messages:");
685 for (i
= 0; i
< DUMPLOG_MSG_COUNT
; ++i
) {
686 strp
= &saved_plines
[DUMPLOG_MSG_COUNT
- 1 - i
];
688 copynchars(&buf
[1], *strp
, BUFSZ
- 1 - 1);
690 #ifdef FREE_ALL_MEMORY
691 free(*strp
), *strp
= 0;
707 if (!iflags
.in_dumplog
)
712 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
713 makeknown(obj
->otyp
);
714 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
715 if (Is_container(obj
) || obj
->otyp
== STATUE
)
716 obj
->cknown
= obj
->lknown
= 1;
719 Sprintf(pbuf
, "%s, %s %s %s %s", plname
,
720 aligns
[1 - u
.ualign
.type
].adj
,
721 genders
[flags
.female
].adj
,
723 (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
);
728 putstr(0, 0, do_statusline1());
729 putstr(0, 0, do_statusline2());
734 putstr(0, 0, "Inventory:");
735 display_inventory((char *) 0, TRUE
);
736 container_contents(invent
, TRUE
, TRUE
, FALSE
);
737 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
738 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
741 list_vanquished('y', FALSE
);
743 list_genocided('a', FALSE
);
745 show_conduct((how
>= PANICKED
) ? 1 : 2);
747 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
749 dump_redirect(FALSE
);
760 char c
= '\0', defquery
;
764 if (invent
&& !done_stopprint
) {
766 Sprintf(qbuf
, "Do you want to see what you had when you %s?",
767 (how
== QUIT
) ? "quit" : "died");
769 Strcpy(qbuf
, "Do you want your possessions identified?");
771 ask
= should_query_disclose_option('i', &defquery
);
772 c
= ask
? yn_function(qbuf
, ynqchars
, defquery
) : defquery
;
776 for (obj
= invent
; obj
; obj
= obj
->nobj
) {
777 makeknown(obj
->otyp
);
778 obj
->known
= obj
->bknown
= obj
->dknown
= obj
->rknown
= 1;
779 if (Is_container(obj
) || obj
->otyp
== STATUE
)
780 obj
->cknown
= obj
->lknown
= 1;
782 (void) display_inventory((char *) 0, TRUE
);
783 container_contents(invent
, TRUE
, TRUE
, FALSE
);
789 if (!done_stopprint
) {
790 ask
= should_query_disclose_option('a', &defquery
);
791 c
= ask
? yn_function("Do you want to see your attributes?", ynqchars
,
795 enlightenment((BASICENLIGHTENMENT
| MAGICENLIGHTENMENT
),
796 (how
>= PANICKED
) ? ENL_GAMEOVERALIVE
802 if (!done_stopprint
) {
803 ask
= should_query_disclose_option('v', &defquery
);
804 list_vanquished(defquery
, ask
);
807 if (!done_stopprint
) {
808 ask
= should_query_disclose_option('g', &defquery
);
809 list_genocided(defquery
, ask
);
812 if (!done_stopprint
) {
813 ask
= should_query_disclose_option('c', &defquery
);
814 c
= ask
? yn_function("Do you want to see your conduct?", ynqchars
,
818 show_conduct((how
>= PANICKED
) ? 1 : 2);
823 if (!done_stopprint
) {
824 ask
= should_query_disclose_option('o', &defquery
);
825 c
= ask
? yn_function("Do you want to see the dungeon overview?",
829 show_overview((how
>= PANICKED
) ? 1 : 2, how
);
835 /* try to get the player back in a viable state after being killed */
840 int uhpmin
= max(2 * u
.ulevel
, 10);
842 if (u
.uhpmax
< uhpmin
)
846 if (u
.uhunger
< 500) {
850 /* cure impending doom of sickness hero won't have time to fix */
851 if ((Sick
& TIMEOUT
) == 1L) {
853 set_itimeout(&Sick
, 0L);
857 nomovemsg
= "You survived that attempt on your life.";
863 if (u
.utrap
&& u
.utraptype
== TT_LAVA
)
866 u
.ugrave_arise
= NON_PM
;
869 if (!context
.mon_moving
)
874 * Get valuables from the given list. Revised code: the list always remains
879 struct obj
*list
; /* inventory or container contents */
881 register struct obj
*obj
;
884 /* find amulets and gems, ignoring all artifacts */
885 for (obj
= list
; obj
; obj
= obj
->nobj
)
886 if (Has_contents(obj
)) {
887 get_valuables(obj
->cobj
);
888 } else if (obj
->oartifact
) {
890 } else if (obj
->oclass
== AMULET_CLASS
) {
891 i
= obj
->otyp
- FIRST_AMULET
;
892 if (!amulets
[i
].count
) {
893 amulets
[i
].count
= obj
->quan
;
894 amulets
[i
].typ
= obj
->otyp
;
896 amulets
[i
].count
+= obj
->quan
; /* always adds one */
897 } else if (obj
->oclass
== GEM_CLASS
&& obj
->otyp
< LUCKSTONE
) {
898 i
= min(obj
->otyp
, LAST_GEM
+ 1) - FIRST_GEM
;
899 if (!gems
[i
].count
) {
900 gems
[i
].count
= obj
->quan
;
901 gems
[i
].typ
= obj
->otyp
;
903 gems
[i
].count
+= obj
->quan
;
909 * Sort collected valuables, most frequent to least. We could just
910 * as easily use qsort, but we don't care about efficiency here.
913 sort_valuables(list
, size
)
914 struct valuable_data list
[];
915 int size
; /* max value is less than 20 */
918 struct valuable_data ltmp
;
920 /* move greater quantities to the front of the list */
921 for (i
= 1; i
< size
; i
++) {
922 if (list
[i
].count
== 0)
923 continue; /* empty slot */
924 ltmp
= list
[i
]; /* structure copy */
925 for (j
= i
; j
> 0; --j
)
926 if (list
[j
- 1].count
>= ltmp
.count
)
929 list
[j
] = list
[j
- 1];
939 odds_and_ends(list
, what
)
944 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
946 case CAT_CHECK
: /* Schroedinger's Cat */
947 /* Ascending is deterministic */
948 if (SchroedingersBox(otmp
))
952 if (Has_contents(otmp
))
953 return odds_and_ends(otmp
->cobj
, what
);
958 /* called twice; first to calculate total, then to list relevant items */
960 artifact_score(list
, counting
, endwin
)
962 boolean counting
; /* true => add up points; false => display them */
968 short dummy
; /* object type returned by artifact_name() */
970 for (otmp
= list
; otmp
; otmp
= otmp
->nobj
) {
971 if (otmp
->oartifact
|| otmp
->otyp
== BELL_OF_OPENING
972 || otmp
->otyp
== SPE_BOOK_OF_THE_DEAD
973 || otmp
->otyp
== CANDELABRUM_OF_INVOCATION
) {
974 value
= arti_cost(otmp
); /* zorkmid value */
975 points
= value
* 5 / 2; /* score value */
977 nowrap_add(u
.urexp
, points
);
979 makeknown(otmp
->otyp
);
980 otmp
->known
= otmp
->dknown
= otmp
->bknown
= otmp
->rknown
= 1;
981 /* assumes artifacts don't have quan > 1 */
982 Sprintf(pbuf
, "%s%s (worth %ld %s and %ld points)",
983 the_unique_obj(otmp
) ? "The " : "",
984 otmp
->oartifact
? artifact_name(xname(otmp
), &dummy
)
985 : OBJ_NAME(objects
[otmp
->otyp
]),
986 value
, currency(value
), points
);
987 putstr(endwin
, 0, pbuf
);
990 if (Has_contents(otmp
))
991 artifact_score(otmp
->cobj
, counting
, endwin
);
995 /* Be careful not to call panic from here! */
1000 if (how
== TRICKED
) {
1001 if (killer
.name
[0]) {
1002 paniclog("trickery", killer
.name
);
1006 You("are a very tricky wizard, it seems.");
1010 if (program_state
.panicking
1011 #ifdef HANGUPHANDLING
1012 || program_state
.done_hup
1015 /* skip status update if panicking or disconnected */
1016 context
.botl
= context
.botlx
= FALSE
;
1018 /* otherwise force full status update */
1019 context
.botlx
= TRUE
;
1023 if (how
== ASCENDED
|| (!killer
.name
[0] && how
== GENOCIDED
))
1024 killer
.format
= NO_KILLER_PREFIX
;
1025 /* Avoid killed by "a" burning or "a" starvation */
1026 if (!killer
.name
[0] && (how
== STARVING
|| how
== BURNING
))
1027 killer
.format
= KILLED_BY
;
1028 if (!killer
.name
[0] || how
>= PANICKED
)
1029 Strcpy(killer
.name
, deaths
[how
]);
1033 if (Lifesaved
&& (how
<= GENOCIDED
)) {
1034 pline("But wait...");
1035 makeknown(AMULET_OF_LIFE_SAVING
);
1036 Your("medallion %s!", !Blind
? "begins to glow" : "feels warm");
1039 You_feel("much better!");
1040 pline_The("medallion crumbles to dust!");
1044 (void) adjattrib(A_CON
, -1, TRUE
);
1046 if (how
== GENOCIDED
) {
1047 pline("Unfortunately you are still genocided...");
1054 if ((wizard
|| discover
) && (how
<= GENOCIDED
)
1055 && !paranoid_query(ParanoidDie
, "Die?")) {
1056 pline("OK, so you don't %s.", (how
== CHOKING
) ? "choke" : "die");
1065 /* separated from done() in order to specify the __noreturn__ attribute */
1072 winid endwin
= WIN_ERR
;
1073 boolean bones_ok
, have_windows
= iflags
.window_inited
;
1074 struct obj
*corpse
= (struct obj
*) 0;
1080 * The game is now over...
1082 program_state
.gameover
= 1;
1083 /* in case of a subsequent panic(), there's no point trying to save */
1084 program_state
.something_worth_saving
= 0;
1085 /* render vision subsystem inoperative */
1086 iflags
.vision_inited
= 0;
1088 /* might have been killed while using a disposable item, so make sure
1089 it's gone prior to inventory disclosure and creation of bones data */
1091 /* maybe not on object lists; if an active light source, would cause
1092 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1093 if (thrownobj
&& thrownobj
->where
== OBJ_FREE
)
1094 dealloc_obj(thrownobj
);
1095 if (kickedobj
&& kickedobj
->where
== OBJ_FREE
)
1096 dealloc_obj(kickedobj
);
1098 /* remember time of death here instead of having bones, rip, and
1099 topten figure it out separately and possibly getting different
1100 time or even day if player is slow responding to --More-- */
1101 urealtime
.finish_time
= endtime
= getnow();
1102 urealtime
.realtime
+= (long) (endtime
- urealtime
.start_timing
);
1104 dump_open_log(endtime
);
1105 /* Sometimes you die on the first move. Life's not fair.
1106 * On those rare occasions you get hosed immediately, go out
1107 * smiling... :-) -3.
1109 if (moves
<= 1 && how
< PANICKED
) /* You die... --More-- */
1110 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1113 wait_synch(); /* flush screen output */
1115 (void) signal(SIGINT
, (SIG_RET_TYPE
) done_intr
);
1116 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1117 (void) signal(SIGQUIT
, (SIG_RET_TYPE
) done_intr
);
1118 sethanguphandler(done_hangup
);
1120 #endif /* NO_SIGNAL */
1122 bones_ok
= (how
< GENOCIDED
) && can_make_bones();
1124 if (bones_ok
&& launch_in_progress())
1125 force_launch_placement();
1127 /* maintain ugrave_arise even for !bones_ok */
1128 if (how
== PANICKED
)
1129 u
.ugrave_arise
= (NON_PM
- 3); /* no corpse, no grave */
1130 else if (how
== BURNING
|| how
== DISSOLVED
) /* corpse burns up too */
1131 u
.ugrave_arise
= (NON_PM
- 2); /* leave no corpse */
1132 else if (how
== STONING
)
1133 u
.ugrave_arise
= (NON_PM
- 1); /* statue instead of corpse */
1134 else if (how
== TURNED_SLIME
)
1135 u
.ugrave_arise
= PM_GREEN_SLIME
;
1137 /* if pets will contribute to score, populate mydogs list now
1138 (bones creation isn't a factor, but pline() messaging is) */
1139 if (how
== ESCAPED
|| how
== ASCENDED
)
1143 killer
.format
= NO_KILLER_PREFIX
;
1146 u
.umortality
++; /* skipped above when how==QUIT */
1147 Strcpy(killer
.name
, "quit while already on Charon's boat");
1150 if (how
== ESCAPED
|| how
== PANICKED
)
1151 killer
.format
= NO_KILLER_PREFIX
;
1153 fixup_death(how
); /* actually, fixup multi_reason */
1155 if (how
!= PANICKED
) {
1156 /* these affect score and/or bones, but avoid them during panic */
1157 taken
= paybill((how
== ESCAPED
) ? -1 : (how
!= QUIT
));
1161 taken
= FALSE
; /* lint; assert( !bones_ok ); */
1166 display_nhwindow(WIN_MESSAGE
, FALSE
);
1168 if (strcmp(flags
.end_disclose
, "none") && how
!= PANICKED
)
1169 disclose(how
, taken
);
1171 dump_everything(how
);
1173 /* finish_paybill should be called after disclosure but before bones */
1174 if (bones_ok
&& taken
)
1177 /* grave creation should be after disclosure so it doesn't have
1178 this grave in the current level's features for #overview */
1179 if (bones_ok
&& u
.ugrave_arise
== NON_PM
1180 && !(mvitals
[u
.umonnum
].mvflags
& G_NOCORPSE
)) {
1181 int mnum
= u
.umonnum
;
1184 /* Base corpse on race when not poly'd since original
1185 * u.umonnum is based on role, and all role monsters
1188 mnum
= (flags
.female
&& urace
.femalenum
!= NON_PM
)
1192 corpse
= mk_named_object(CORPSE
, &mons
[mnum
], u
.ux
, u
.uy
, plname
);
1193 Sprintf(pbuf
, "%s, ", plname
);
1194 formatkiller(eos(pbuf
), sizeof pbuf
- strlen(pbuf
), how
, TRUE
);
1195 make_grave(u
.ux
, u
.uy
, pbuf
);
1197 pbuf
[0] = '\0'; /* clear grave text; also lint suppression */
1199 /* calculate score, before creating bones [container gold] */
1201 int deepest
= deepest_lev_reached(FALSE
);
1203 umoney
= money_cnt(invent
);
1205 umoney
+= hidden_gold(); /* accumulate gold from containers */
1206 tmp
= umoney
- tmp
; /* net gain */
1212 tmp
+= 50L * (long) (deepest
- 1);
1214 tmp
+= 1000L * (long) ((deepest
> 30) ? 10 : deepest
- 20);
1215 nowrap_add(u
.urexp
, tmp
);
1217 /* ascension gives a score bonus iff offering to original deity */
1218 if (how
== ASCENDED
&& u
.ualign
.type
== u
.ualignbase
[A_ORIGINAL
]) {
1219 /* retaining original alignment: score *= 2;
1220 converting, then using helm-of-OA to switch back: *= 1.5 */
1221 tmp
= (u
.ualignbase
[A_CURRENT
] == u
.ualignbase
[A_ORIGINAL
])
1224 nowrap_add(u
.urexp
, tmp
);
1228 if (u
.ugrave_arise
>= LOW_PM
&& u
.ugrave_arise
!= PM_GREEN_SLIME
) {
1229 /* give this feedback even if bones aren't going to be created,
1230 so that its presence or absence doesn't tip off the player to
1231 new bones or their lack; it might be a lie if makemon fails */
1232 Your("body rises from the dead as %s...",
1233 an(mons
[u
.ugrave_arise
].mname
));
1234 display_nhwindow(WIN_MESSAGE
, FALSE
);
1238 if (!wizard
|| paranoid_query(ParanoidBones
, "Save bones?"))
1239 savebones(how
, endtime
, corpse
);
1240 /* corpse may be invalid pointer now so
1241 ensure that it isn't used again */
1242 corpse
= (struct obj
*) 0;
1245 /* update gold for the rip output, which can't use hidden_gold()
1246 (containers will be gone by then if bones just got saved...) */
1247 done_money
= umoney
;
1249 /* clean up unneeded windows */
1252 free_pickinv_cache(); /* extra persistent window if perm_invent */
1253 if (WIN_INVEN
!= WIN_ERR
)
1254 destroy_nhwindow(WIN_INVEN
), WIN_INVEN
= WIN_ERR
;
1255 display_nhwindow(WIN_MESSAGE
, TRUE
);
1256 destroy_nhwindow(WIN_MAP
), WIN_MAP
= WIN_ERR
;
1257 #ifndef STATUS_VIA_WINDOWPORT
1258 destroy_nhwindow(WIN_STATUS
), WIN_STATUS
= WIN_ERR
;
1260 destroy_nhwindow(WIN_MESSAGE
), WIN_MESSAGE
= WIN_ERR
;
1262 if (!done_stopprint
|| flags
.tombstone
)
1263 endwin
= create_nhwindow(NHW_TEXT
);
1265 if (how
< GENOCIDED
&& flags
.tombstone
&& endwin
!= WIN_ERR
)
1266 outrip(endwin
, how
, endtime
);
1268 done_stopprint
= 1; /* just avoid any more output */
1271 dump_redirect(TRUE
);
1272 genl_outrip(0, how
, endtime
);
1273 dump_redirect(FALSE
);
1275 if (u
.uhave
.amulet
) {
1276 Strcat(killer
.name
, " (with the Amulet)");
1277 } else if (how
== ESCAPED
) {
1278 if (Is_astralevel(&u
.uz
)) /* offered Amulet to wrong deity */
1279 Strcat(killer
.name
, " (in celestial disgrace)");
1280 else if (carrying(FAKE_AMULET_OF_YENDOR
))
1281 Strcat(killer
.name
, " (with a fake Amulet)");
1282 /* don't bother counting to see whether it should be plural */
1285 Sprintf(pbuf
, "%s %s the %s...", Goodbye(), plname
,
1287 ? (const char *) ((flags
.female
&& urole
.name
.f
)
1290 : (const char *) (flags
.female
? "Demigoddess" : "Demigod"));
1291 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1292 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1294 if (how
== ESCAPED
|| how
== ASCENDED
) {
1297 register struct val_list
*val
;
1300 for (val
= valuables
; val
->list
; val
++)
1301 for (i
= 0; i
< val
->size
; i
++) {
1302 val
->list
[i
].count
= 0L;
1304 get_valuables(invent
);
1306 /* add points for collected valuables */
1307 for (val
= valuables
; val
->list
; val
++)
1308 for (i
= 0; i
< val
->size
; i
++)
1309 if (val
->list
[i
].count
!= 0L) {
1310 tmp
= val
->list
[i
].count
1311 * (long) objects
[val
->list
[i
].typ
].oc_cost
;
1312 nowrap_add(u
.urexp
, tmp
);
1315 /* count the points for artifacts */
1316 artifact_score(invent
, TRUE
, endwin
);
1318 dump_redirect(TRUE
);
1319 artifact_score(invent
, TRUE
, endwin
);
1320 dump_redirect(FALSE
);
1323 viz_array
[0][0] |= IN_SIGHT
; /* need visibility for naming */
1325 Strcpy(pbuf
, "You");
1326 if (!Schroedingers_cat
) /* check here in case disclosure was off */
1327 Schroedingers_cat
= odds_and_ends(invent
, CAT_CHECK
);
1328 if (Schroedingers_cat
) {
1329 int mhp
, m_lev
= adj_lev(&mons
[PM_HOUSECAT
]);
1331 nowrap_add(u
.urexp
, mhp
);
1332 Strcat(eos(pbuf
), " and Schroedinger's cat");
1336 Sprintf(eos(pbuf
), " and %s", mon_nam(mtmp
));
1338 nowrap_add(u
.urexp
, mtmp
->mhp
);
1341 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1346 Sprintf(eos(pbuf
), "%s with %ld point%s,",
1347 how
== ASCENDED
? "went to your reward"
1348 : "escaped from the dungeon",
1349 u
.urexp
, plur(u
.urexp
));
1350 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1352 if (!done_stopprint
)
1353 artifact_score(invent
, FALSE
, endwin
); /* list artifacts */
1355 dump_redirect(TRUE
);
1356 artifact_score(invent
, FALSE
, 0);
1357 dump_redirect(FALSE
);
1360 /* list valuables here */
1361 for (val
= valuables
; val
->list
; val
++) {
1362 sort_valuables(val
->list
, val
->size
);
1363 for (i
= 0; i
< val
->size
&& !done_stopprint
; i
++) {
1364 int typ
= val
->list
[i
].typ
;
1365 long count
= val
->list
[i
].count
;
1369 if (objects
[typ
].oc_class
!= GEM_CLASS
|| typ
<= LAST_GEM
) {
1370 otmp
= mksobj(typ
, FALSE
, FALSE
);
1371 makeknown(otmp
->otyp
);
1372 otmp
->known
= 1; /* for fake amulets */
1373 otmp
->dknown
= 1; /* seen it (blindness fix) */
1374 if (has_oname(otmp
))
1377 Sprintf(pbuf
, "%8ld %s (worth %ld %s),", count
,
1378 xname(otmp
), count
* (long) objects
[typ
].oc_cost
,
1380 obfree(otmp
, (struct obj
*) 0);
1382 Sprintf(pbuf
, "%8ld worthless piece%s of colored glass,",
1383 count
, plur(count
));
1385 dump_forward_putstr(endwin
, 0, pbuf
, 0);
1390 /* did not escape or ascend */
1391 if (u
.uz
.dnum
== 0 && u
.uz
.dlevel
<= 0) {
1392 /* level teleported out of the dungeon; `how' is DIED,
1393 due to falling or to "arriving at heaven prematurely" */
1394 Sprintf(pbuf
, "You %s beyond the confines of the dungeon",
1395 (u
.uz
.dlevel
< 0) ? "passed away" : ends
[how
]);
1397 /* more conventional demise */
1398 const char *where
= dungeons
[u
.uz
.dnum
].dname
;
1400 if (Is_astralevel(&u
.uz
))
1401 where
= "The Astral Plane";
1402 Sprintf(pbuf
, "You %s in %s", ends
[how
], where
);
1403 if (!In_endgame(&u
.uz
) && !Is_knox(&u
.uz
))
1404 Sprintf(eos(pbuf
), " on dungeon level %d",
1405 In_quest(&u
.uz
) ? dunlev(&u
.uz
) : depth(&u
.uz
));
1408 Sprintf(eos(pbuf
), " with %ld point%s,", u
.urexp
, plur(u
.urexp
));
1409 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1412 Sprintf(pbuf
, "and %ld piece%s of gold, after %ld move%s.", umoney
,
1413 plur(umoney
), moves
, plur(moves
));
1414 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1416 "You were level %d with a maximum of %d hit point%s when you %s.",
1417 u
.ulevel
, u
.uhpmax
, plur(u
.uhpmax
), ends
[how
]);
1418 dump_forward_putstr(endwin
, 0, pbuf
, done_stopprint
);
1419 dump_forward_putstr(endwin
, 0, "", done_stopprint
);
1420 if (!done_stopprint
)
1421 display_nhwindow(endwin
, TRUE
);
1422 if (endwin
!= WIN_ERR
)
1423 destroy_nhwindow(endwin
);
1426 /* "So when I die, the first thing I will see in Heaven is a
1428 if (have_windows
&& !iflags
.toptenwin
)
1429 exit_nhwindows((char *) 0), have_windows
= FALSE
;
1430 topten(how
, endtime
);
1432 exit_nhwindows((char *) 0);
1434 if (done_stopprint
) {
1438 terminate(EXIT_SUCCESS
);
1442 container_contents(list
, identified
, all_containers
, reportempty
)
1444 boolean identified
, all_containers
, reportempty
;
1446 register struct obj
*box
, *obj
;
1448 boolean cat
, deadcat
;
1450 for (box
= list
; box
; box
= box
->nobj
) {
1451 if (Is_container(box
) || box
->otyp
== STATUE
) {
1452 box
->cknown
= 1; /* we're looking at the contents now */
1455 cat
= deadcat
= FALSE
;
1456 if (SchroedingersBox(box
) && !Schroedingers_cat
) {
1457 /* Schroedinger's Cat? */
1458 cat
= odds_and_ends(box
, CAT_CHECK
);
1460 Schroedingers_cat
= TRUE
;
1465 if (box
->otyp
== BAG_OF_TRICKS
) {
1466 continue; /* wrong type of container */
1467 } else if (box
->cobj
) {
1468 winid tmpwin
= create_nhwindow(NHW_MENU
);
1470 sortloot(&box
->cobj
,
1471 (((flags
.sortloot
== 'l' || flags
.sortloot
== 'f')
1472 ? SORTLOOT_LOOT
: 0)
1473 | (flags
.sortpack
? SORTLOOT_PACK
: 0)),
1475 Sprintf(buf
, "Contents of %s:", the(xname(box
)));
1476 putstr(tmpwin
, 0, buf
);
1477 putstr(tmpwin
, 0, "");
1478 for (obj
= box
->cobj
; obj
; obj
= obj
->nobj
) {
1480 makeknown(obj
->otyp
);
1481 obj
->known
= obj
->bknown
= obj
->dknown
1483 if (Is_container(obj
) || obj
->otyp
== STATUE
)
1484 obj
->cknown
= obj
->lknown
= 1;
1486 putstr(tmpwin
, 0, doname(obj
));
1489 putstr(tmpwin
, 0, "Schroedinger's cat");
1491 putstr(tmpwin
, 0, "Schroedinger's dead cat");
1492 display_nhwindow(tmpwin
, TRUE
);
1493 destroy_nhwindow(tmpwin
);
1495 container_contents(box
->cobj
, identified
, TRUE
,
1497 } else if (cat
|| deadcat
) {
1498 pline("%s Schroedinger's %scat!", Tobjnam(box
, "contain"),
1499 deadcat
? "dead " : "");
1500 display_nhwindow(WIN_MESSAGE
, FALSE
);
1501 } else if (reportempty
) {
1502 pline("%s is empty.", upstart(thesimpleoname(box
)));
1503 display_nhwindow(WIN_MESSAGE
, FALSE
);
1506 if (!all_containers
)
1511 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1516 program_state
.in_moveloop
= 0; /* won't be returning to normal play */
1518 getreturn("to exit");
1520 /* don't bother to try to release memory if we're in panic mode, to
1521 avoid trouble in case that happens to be due to memory problems */
1522 if (!program_state
.panicking
) {
1529 * This is liable to draw a warning if compiled with gcc, but it's
1530 * more important to flag panic() -> really_done() -> terminate()
1531 * as __noreturn__ then to avoid the warning.
1533 /* don't call exit() if already executing within an exit handler;
1534 that would cancel any other pending user-mode handlers */
1535 if (program_state
.exiting
)
1538 program_state
.exiting
= 1;
1539 nethack_exit(status
);
1542 extern const int monstr
[];
1544 static const char *vanqorders
[] = {
1545 "traditional: by monster level, by internal monster index",
1546 #define VANQ_MLVL_MNDX 0
1547 "by monster toughness, by internal monster index",
1548 #define VANQ_MSTR_MNDX 1
1549 "alphabetically, first unique monsters, then others",
1550 #define VANQ_ALPHA_SEP 2
1551 "alphabetically, unique monsters and others intermixed",
1552 #define VANQ_ALPHA_MIX 3
1553 "by monster class, high to low level within class",
1554 #define VANQ_MCLS_HTOL 4
1555 "by monster class, low to high level within class",
1556 #define VANQ_MCLS_LTOH 5
1557 "by count, high to low, by internal index within tied count",
1558 #define VANQ_COUNT_H_L 6
1559 "by count, low to high, by internal index within tied count",
1560 #define VANQ_COUNT_L_H 7
1562 static int vanq_sortmode
= VANQ_MLVL_MNDX
;
1564 STATIC_PTR
int CFDECLSPEC
1565 vanqsort_cmp(vptr1
, vptr2
)
1566 const genericptr vptr1
;
1567 const genericptr vptr2
;
1569 int indx1
= *(short *) vptr1
, indx2
= *(short *) vptr2
,
1570 mlev1
, mlev2
, mstr1
, mstr2
, uniq1
, uniq2
, died1
, died2
, res
;
1571 const char *name1
, *name2
, *punct
;
1574 switch (vanq_sortmode
) {
1576 case VANQ_MLVL_MNDX
:
1577 /* sort by monster level */
1578 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1579 res
= mlev2
- mlev1
; /* mlevel high to low */
1581 case VANQ_MSTR_MNDX
:
1582 /* sort by monster toughness */
1583 mstr1
= monstr
[indx1
], mstr2
= monstr
[indx2
];
1584 res
= mstr2
- mstr1
; /* monstr high to low */
1586 case VANQ_ALPHA_SEP
:
1587 uniq1
= ((mons
[indx1
].geno
& G_UNIQ
) && indx1
!= PM_HIGH_PRIEST
);
1588 uniq2
= ((mons
[indx2
].geno
& G_UNIQ
) && indx2
!= PM_HIGH_PRIEST
);
1589 if (uniq1
^ uniq2
) { /* one or other uniq, but not both */
1590 res
= uniq2
- uniq1
;
1592 } /* else both unique or neither unique */
1594 case VANQ_ALPHA_MIX
:
1595 name1
= mons
[indx1
].mname
, name2
= mons
[indx2
].mname
;
1596 res
= strcmpi(name1
, name2
); /* caseblind alhpa, low to high */
1598 case VANQ_MCLS_HTOL
:
1599 case VANQ_MCLS_LTOH
:
1600 /* mons[].mlet is a small integer, 1..N, of type plain char;
1601 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1602 an inappropriate result when mlet2 is greater than mlet1,
1603 so force our copies (mcls1, mcls2) to be signed */
1604 mcls1
= (schar
) mons
[indx1
].mlet
, mcls2
= (schar
) mons
[indx2
].mlet
;
1605 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1606 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1607 punctuation characters are used for classes beyond those */
1608 if (mcls1
> S_ZOMBIE
&& mcls2
> S_ZOMBIE
) {
1609 /* force a specific order to the punctuation classes that's
1610 different from the internal order;
1611 internal order is ok if neither or just one is punctuation
1612 since letters have lower values so come out before punct */
1613 static const char punctclasses
[] = {
1614 S_LIZARD
, S_EEL
, S_GOLEM
, S_GHOST
, S_DEMON
, S_HUMAN
, '\0'
1617 if ((punct
= index(punctclasses
, mcls1
)) != 0)
1618 mcls1
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1619 if ((punct
= index(punctclasses
, mcls2
)) != 0)
1620 mcls2
= (schar
) (S_ZOMBIE
+ 1 + (int) (punct
- punctclasses
));
1622 res
= mcls1
- mcls2
; /* class */
1624 mlev1
= mons
[indx1
].mlevel
, mlev2
= mons
[indx2
].mlevel
;
1625 res
= mlev1
- mlev2
; /* mlevel low to high */
1626 if (vanq_sortmode
== VANQ_MCLS_HTOL
)
1627 res
= -res
; /* mlevel high to low */
1630 case VANQ_COUNT_H_L
:
1631 case VANQ_COUNT_L_H
:
1632 died1
= mvitals
[indx1
].died
, died2
= mvitals
[indx2
].died
;
1633 res
= died2
- died1
; /* dead count high to low */
1634 if (vanq_sortmode
== VANQ_COUNT_L_H
)
1635 res
= -res
; /* dead count low to high */
1638 /* tiebreaker: internal mons[] index */
1640 res
= indx1
- indx2
; /* mndx low to high */
1644 /* returns -1 if cancelled via ESC */
1649 menu_item
*selected
;
1653 tmpwin
= create_nhwindow(NHW_MENU
);
1655 any
= zeroany
; /* zero out all bits */
1656 for (i
= 0; i
< SIZE(vanqorders
); i
++) {
1657 if (i
== VANQ_ALPHA_MIX
|| i
== VANQ_MCLS_HTOL
) /* skip these */
1660 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, vanqorders
[i
],
1661 (i
== vanq_sortmode
) ? MENU_SELECTED
: MENU_UNSELECTED
);
1663 end_menu(tmpwin
, "Sort order for vanquished monster counts");
1665 n
= select_menu(tmpwin
, PICK_ONE
, &selected
);
1666 destroy_nhwindow(tmpwin
);
1668 choice
= selected
[0].item
.a_int
- 1;
1669 /* skip preselected entry if we have more than one item chosen */
1670 if (n
> 1 && choice
== vanq_sortmode
)
1671 choice
= selected
[1].item
.a_int
- 1;
1672 free((genericptr_t
) selected
);
1673 vanq_sortmode
= choice
;
1675 return (n
< 0) ? -1 : vanq_sortmode
;
1678 /* #vanquished command */
1682 list_vanquished('a', FALSE
);
1686 /* high priests aren't unique but are flagged as such to simplify something */
1687 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1688 && mndx != PM_HIGH_PRIEST)
1691 list_vanquished(defquery
, ask
)
1697 unsigned ntypes
, ni
;
1698 long total_killed
= 0L;
1700 short mindx
[NUMMONS
];
1701 char c
, buf
[BUFSZ
], buftoo
[BUFSZ
];
1703 /* get totals first */
1705 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1706 if ((nkilled
= (int) mvitals
[i
].died
) == 0)
1708 mindx
[ntypes
++] = i
;
1709 total_killed
+= (long) nkilled
;
1712 /* vanquished creatures list;
1713 * includes all dead monsters, not just those killed by the player
1716 char mlet
, prev_mlet
= 0; /* used as small integer, not character */
1717 boolean class_header
, uniq_header
, was_uniq
= FALSE
;
1719 c
= ask
? yn_function(
1720 "Do you want an account of creatures vanquished?",
1721 ynaqchars
, defquery
)
1725 if (c
== 'y' || c
== 'a') {
1726 if (c
== 'a') { /* ask player to choose sort order */
1727 /* choose value for vanq_sortmode via menu; ESC cancels list
1728 of vanquished monsters but does not set 'done_stopprint' */
1729 if (set_vanq_order() < 0)
1732 uniq_header
= (vanq_sortmode
== VANQ_ALPHA_SEP
);
1733 class_header
= (vanq_sortmode
== VANQ_MCLS_LTOH
1734 || vanq_sortmode
== VANQ_MCLS_HTOL
);
1736 klwin
= create_nhwindow(NHW_MENU
);
1737 putstr(klwin
, 0, "Vanquished creatures:");
1738 putstr(klwin
, 0, "");
1740 qsort((genericptr_t
) mindx
, ntypes
, sizeof *mindx
, vanqsort_cmp
);
1741 for (ni
= 0; ni
< ntypes
; ni
++) {
1743 nkilled
= mvitals
[i
].died
;
1744 mlet
= mons
[i
].mlet
;
1745 if (class_header
&& mlet
!= prev_mlet
) {
1746 Strcpy(buf
, def_monsyms
[(int) mlet
].explain
);
1747 putstr(klwin
, ask
? 0 : iflags
.menu_headings
,
1751 if (UniqCritterIndx(i
)) {
1752 Sprintf(buf
, "%s%s",
1753 !type_is_pname(&mons
[i
]) ? "the " : "",
1758 Sprintf(eos(buf
), " (twice)");
1761 Sprintf(eos(buf
), " (thrice)");
1764 Sprintf(eos(buf
), " (%d times)", nkilled
);
1770 if (uniq_header
&& was_uniq
) {
1771 putstr(klwin
, 0, "");
1774 /* trolls or undead might have come back,
1775 but we don't keep track of that */
1777 Strcpy(buf
, an(mons
[i
].mname
));
1779 Sprintf(buf
, "%3d %s", nkilled
,
1780 makeplural(mons
[i
].mname
));
1782 /* number of leading spaces to match 3 digit prefix */
1783 pfx
= !strncmpi(buf
, "the ", 3) ? 0
1784 : !strncmpi(buf
, "an ", 3) ? 1
1785 : !strncmpi(buf
, "a ", 2) ? 2
1786 : !digit(buf
[2]) ? 4 : 0;
1789 Sprintf(buftoo
, "%*s%s", pfx
, "", buf
);
1790 putstr(klwin
, 0, buftoo
);
1793 * if (Hallucination)
1794 * putstr(klwin, 0, "and a partridge in a pear tree");
1797 putstr(klwin
, 0, "");
1798 Sprintf(buf
, "%ld creatures vanquished.", total_killed
);
1799 putstr(klwin
, 0, buf
);
1801 display_nhwindow(klwin
, TRUE
);
1802 destroy_nhwindow(klwin
);
1804 } else if (defquery
== 'a') {
1805 /* #dovanquished rather than final disclosure, so pline() is ok */
1806 pline("No monsters have been vanquished.");
1810 /* number of monster species which have been genocided */
1816 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1817 if (mvitals
[i
].mvflags
& G_GENOD
) {
1819 if (UniqCritterIndx(i
))
1820 impossible("unique creature '%d: %s' genocided?",
1832 for (i
= LOW_PM
; i
< NUMMONS
; ++i
) {
1833 if (UniqCritterIndx(i
))
1835 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1842 list_genocided(defquery
, ask
)
1847 int ngenocided
, nextinct
;
1852 ngenocided
= num_genocides();
1853 nextinct
= num_extinct();
1855 /* genocided or extinct species list */
1856 if (ngenocided
!= 0 || nextinct
!= 0) {
1857 Sprintf(buf
, "Do you want a list of %sspecies%s%s?",
1858 (nextinct
&& !ngenocided
) ? "extinct " : "",
1859 (ngenocided
) ? " genocided" : "",
1860 (nextinct
&& ngenocided
) ? " and extinct" : "");
1861 c
= ask
? yn_function(buf
, ynqchars
, defquery
) : defquery
;
1865 klwin
= create_nhwindow(NHW_MENU
);
1866 Sprintf(buf
, "%s%s species:",
1867 (ngenocided
) ? "Genocided" : "Extinct",
1868 (nextinct
&& ngenocided
) ? " or extinct" : "");
1869 putstr(klwin
, 0, buf
);
1870 putstr(klwin
, 0, "");
1872 for (i
= LOW_PM
; i
< NUMMONS
; i
++) {
1873 /* uniques can't be genocided but can become extinct;
1874 however, they're never reported as extinct, so skip them */
1875 if (UniqCritterIndx(i
))
1877 if (mvitals
[i
].mvflags
& G_GONE
) {
1878 Strcpy(buf
, makeplural(mons
[i
].mname
));
1880 * "Extinct" is unfortunate terminology. A species
1881 * is marked extinct when its birth limit is reached,
1882 * but there might be members of the species still
1883 * alive, contradicting the meaning of the word.
1885 if ((mvitals
[i
].mvflags
& G_GONE
) == G_EXTINCT
)
1886 Strcat(buf
, " (extinct)");
1887 putstr(klwin
, 0, buf
);
1890 putstr(klwin
, 0, "");
1891 if (ngenocided
> 0) {
1892 Sprintf(buf
, "%d species genocided.", ngenocided
);
1893 putstr(klwin
, 0, buf
);
1896 Sprintf(buf
, "%d species extinct.", nextinct
);
1897 putstr(klwin
, 0, buf
);
1900 display_nhwindow(klwin
, TRUE
);
1901 destroy_nhwindow(klwin
);
1906 /* set a delayed killer, ensure non-delayed killer is cleared out */
1908 delayed_killer(id
, format
, killername
)
1911 const char *killername
;
1913 struct kinfo
*k
= find_delayed_killer(id
);
1915 if (k
== (struct kinfo
*) 0) {
1916 /* no match, add a new delayed killer to the list */
1917 k
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
1918 (void) memset((genericptr_t
)k
, 0, sizeof(struct kinfo
));
1920 k
->next
= killer
.next
;
1925 Strcpy(k
->name
, killername
? killername
: "");
1930 find_delayed_killer(id
)
1935 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1943 dealloc_killer(kptr
)
1946 struct kinfo
*prev
= &killer
, *k
;
1948 if (kptr
== (struct kinfo
*) 0)
1950 for (k
= killer
.next
; k
!= (struct kinfo
*) 0; k
= k
->next
) {
1956 if (k
== (struct kinfo
*) 0) {
1957 impossible("dealloc_killer not on list");
1959 prev
->next
= k
->next
;
1960 free((genericptr_t
) k
);
1965 save_killers(fd
, mode
)
1971 if (perform_bwrite(mode
)) {
1972 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1973 bwrite(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1976 if (release_data(mode
)) {
1977 while (killer
.next
) {
1978 kptr
= killer
.next
->next
;
1979 free((genericptr_t
) killer
.next
);
1991 for (kptr
= &killer
; kptr
!= (struct kinfo
*) 0; kptr
= kptr
->next
) {
1992 mread(fd
, (genericptr_t
) kptr
, sizeof(struct kinfo
));
1994 kptr
->next
= (struct kinfo
*) alloc(sizeof(struct kinfo
));
2006 while (*p
&& isspace((uchar
) *p
))
2010 while (*p
&& !isspace((uchar
) *p
))
2022 out
+= strlen(out
); /* eos() */
2023 while (*in
&& isspace((uchar
) *in
))
2025 while (*in
&& !isspace((uchar
) *in
))
2032 build_english_list(in
)
2036 int len
= (int) strlen(p
), words
= wordcount(p
);
2038 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2040 len
+= 3 + (words
- 1);
2041 out
= (char *) alloc(len
+ 1);
2042 *out
= '\0'; /* bel_copy1() appends */
2046 impossible("no words in list");
2054 /* "first or second" */
2058 /* "first, second, or third */
2062 } while (--words
> 1);