1 /* NetHack 3.6 questpgr.c $NHDT-Date: 1505172128 2017/09/11 23:22:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.38 $ */
2 /* Copyright 1991, M. Stephenson */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2023 */
8 /* JNetHack may be freely redistributed. See license for details. */
13 /* quest-specific pager routines. */
17 #define QTEXT_FILE "quest.dat"
23 /* from sp_lev.c, for deliver_splev_message() */
24 extern char *lev_message;
26 static void NDECL(dump_qtlist);
27 static void FDECL(Fread, (genericptr_t, int, int, dlb *));
28 STATIC_DCL struct qtmsg *FDECL(construct_qtlist, (long));
29 STATIC_DCL const char *NDECL(intermed);
30 STATIC_DCL struct obj *FDECL(find_qarti, (struct obj *));
31 STATIC_DCL const char *NDECL(neminame);
32 STATIC_DCL const char *NDECL(guardname);
33 STATIC_DCL const char *NDECL(homebase);
34 STATIC_DCL void FDECL(qtext_pronoun, (CHAR_P, CHAR_P));
35 STATIC_DCL struct qtmsg *FDECL(msg_in, (struct qtmsg *, int));
36 STATIC_DCL void FDECL(convert_arg, (CHAR_P));
37 STATIC_DCL void FDECL(convert_line, (char *,char *));
38 STATIC_DCL void FDECL(deliver_by_pline, (struct qtmsg *));
39 STATIC_DCL void FDECL(deliver_by_window, (struct qtmsg *, int));
40 STATIC_DCL boolean FDECL(skip_pager, (BOOLEAN_P));
42 static char cvt_buf[64];
43 static struct qtlists qt_list;
45 /* used by ldrname() and neminame(), then copied into cvt_buf */
46 static char nambuf[sizeof cvt_buf];
48 /* dump the character msg list to check appearance;
49 build with DEBUG enabled and use DEBUGFILES=questpgr.c
50 in sysconf file or environment */
57 if (!explicitdebug(__FILE__))
60 for (msg = qt_list.chrole; msg->msgnum > 0; msg++) {
61 (void) dlb_fseek(msg_file, msg->offset, SEEK_SET);
62 deliver_by_window(msg, NHW_MAP);
69 Fread(ptr, size, nitems, stream)
76 if ((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) {
77 panic("PREMATURE EOF ON QUEST TEXT FILE! Expected %d bytes, got %d",
78 (size * nitems), (size * cnt));
82 STATIC_OVL struct qtmsg *
83 construct_qtlist(hdr_offset)
86 struct qtmsg *msg_list;
89 (void) dlb_fseek(msg_file, hdr_offset, SEEK_SET);
90 Fread(&n_msgs, sizeof(int), 1, msg_file);
91 msg_list = (struct qtmsg *) alloc((unsigned) (n_msgs + 1)
92 * sizeof (struct qtmsg));
97 Fread((genericptr_t) msg_list, n_msgs * sizeof (struct qtmsg), 1,
100 msg_list[n_msgs].msgnum = -1;
108 char qt_classes[N_HDR][LEN_HDR];
109 long qt_offsets[N_HDR];
111 msg_file = dlb_fopen(QTEXT_FILE, RDBMODE);
113 panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE);
116 * Read in the number of classes, then the ID's & offsets for
120 Fread(&n_classes, sizeof (int), 1, msg_file);
121 Fread(&qt_classes[0][0], sizeof (char) * LEN_HDR, n_classes, msg_file);
122 Fread(qt_offsets, sizeof (long), n_classes, msg_file);
125 * Now construct the message lists for quick reference later
126 * on when we are actually paging the messages out.
129 qt_list.common = qt_list.chrole = (struct qtmsg *) 0;
131 for (i = 0; i < n_classes; i++) {
132 if (!strncmp(COMMON_ID, qt_classes[i], LEN_HDR))
133 qt_list.common = construct_qtlist(qt_offsets[i]);
134 else if (!strncmp(urole.filecode, qt_classes[i], LEN_HDR))
135 qt_list.chrole = construct_qtlist(qt_offsets[i]);
136 #if 0 /* UNUSED but available */
137 else if (!strncmp(urace.filecode, qt_classes[i], LEN_HDR))
138 qt_list.chrace = construct_qtlist(qt_offsets[i]);
142 if (!qt_list.common || !qt_list.chrole)
143 impossible("load_qtlist: cannot load quest text.");
145 return; /* no ***DON'T*** close the msg_file */
148 /* called at program exit */
153 (void) dlb_fclose(msg_file), msg_file = 0;
155 free((genericptr_t) qt_list.common), qt_list.common = 0;
157 free((genericptr_t) qt_list.chrole), qt_list.chrole = 0;
167 return urole.questarti;
171 return urole.neminum;
173 return urole.guardnum;
175 impossible("quest_info(%d)", typ);
180 /* return your role leader's name */
184 int i = urole.ldrnum;
187 Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
190 Strcpy(nambuf, mons[i].mname);
195 /* return your intermediate target string */
196 STATIC_OVL const char *
199 return urole.intermed;
203 is_quest_artifact(otmp)
206 return (boolean) (otmp->oartifact == urole.questarti);
209 STATIC_OVL struct obj *
213 struct obj *otmp, *qarti;
215 for (otmp = ochain; otmp; otmp = otmp->nobj) {
216 if (is_quest_artifact(otmp))
218 if (Has_contents(otmp) && (qarti = find_qarti(otmp->cobj)) != 0)
221 return (struct obj *) 0;
224 /* check several object chains for the quest artifact to determine
225 whether it is present on the current level */
227 find_quest_artifact(whichchains)
228 unsigned whichchains;
231 struct obj *qarti = 0;
233 if ((whichchains & (1 << OBJ_INVENT)) != 0)
234 qarti = find_qarti(invent);
235 if (!qarti && (whichchains & (1 << OBJ_FLOOR)) != 0)
236 qarti = find_qarti(fobj);
237 if (!qarti && (whichchains & (1 << OBJ_MINVENT)) != 0)
238 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
239 if (DEADMONSTER(mtmp))
241 if ((qarti = find_qarti(mtmp->minvent)) != 0)
244 if (!qarti && (whichchains & (1 << OBJ_MIGRATING)) != 0) {
245 /* check migrating objects and minvent of migrating monsters */
246 for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) {
247 if (DEADMONSTER(mtmp))
249 if ((qarti = find_qarti(mtmp->minvent)) != 0)
253 qarti = find_qarti(migrating_objs);
255 if (!qarti && (whichchains & (1 << OBJ_BURIED)) != 0)
256 qarti = find_qarti(level.buriedobjlist);
261 /* return your role nemesis' name */
262 STATIC_OVL const char *
265 int i = urole.neminum;
268 Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
271 Strcpy(nambuf, mons[i].mname);
276 STATIC_OVL const char *
277 guardname() /* return your role leader's guard monster name */
279 int i = urole.guardnum;
281 return mons[i].mname;
284 STATIC_OVL const char *
285 homebase() /* return your role leader's location */
287 return urole.homebase;
290 /* replace deity, leader, nemesis, or artifact name with pronoun;
291 overwrites cvt_buf[] */
293 qtext_pronoun(who, which)
294 char who, /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => artifact */
295 which; /* 'h'|'H'|'i'|'I'|'j'|'J' */
299 char lwhich = lowc(which); /* H,I,J -> h,i,j */
302 * Invalid subject (not d,l,n,o) yields neuter, singular result.
304 * For %o, treat all artifacts as neuter; some have plural names,
305 * which genders[] doesn't handle; cvt_buf[] already contains name.
308 && (strstri(cvt_buf, "Eyes ")
309 || strcmpi(cvt_buf, makesingular(cvt_buf)))) {
310 pnoun = (lwhich == 'h') ? "they"
311 : (lwhich == 'i') ? "them"
312 : (lwhich == 'j') ? "their" : "?";
314 g = (who == 'd') ? quest_status.godgend
315 : (who == 'l') ? quest_status.ldrgend
316 : (who == 'n') ? quest_status.nemgend
317 : 2; /* default to neuter */
318 pnoun = (lwhich == 'h') ? genders[g].he
319 : (lwhich == 'i') ? genders[g].him
320 : (lwhich == 'j') ? genders[g].his : "?";
322 Strcpy(cvt_buf, pnoun);
324 /* capitalize for H,I,J */
326 cvt_buf[0] = highc(cvt_buf[0]);
331 STATIC_OVL struct qtmsg *
332 msg_in(qtm_list, msgnum)
333 struct qtmsg *qtm_list;
336 struct qtmsg *qt_msg;
338 for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++)
339 if (qt_msg->msgnum == msgnum)
342 return (struct qtmsg *) 0;
349 register const char *str;
356 str = (flags.female && urole.name.f) ? urole.name.f : urole.name.m;
359 str = rank_of(u.ulevel, Role_switch, flags.female);
362 str = rank_of(MIN_QUEST_LEVEL, Role_switch, flags.female);
366 str = (flags.female) ? "sister" : "brother";
368 str = (flags.female) ? "
\96\85" : "
\92Ã";
372 str = (flags.female) ? "daughter" : "son";
374 str = (flags.female) ? "
\96º" : "
\91§
\8eq";
384 str = the(artiname(urole.questarti));
387 /* shorten "the Foo of Bar" to "the Foo"
388 (buffer returned by the() is modifiable) */
389 char *p = strstri(str, " of ");
403 str = align_gtitle(u.ualignbase[A_ORIGINAL]);
409 str = align_str(u.ualignbase[A_ORIGINAL]);
412 str = align_str(u.ualign.type);
415 str = align_gname(u.ualignbase[A_ORIGINAL]);
418 str = align_gname(A_LAWFUL);
436 str = "
\92\81\8f\98";
440 str = Blind ? "sense" : "see";
442 str = Blind ? "
\8a´
\82¶" : "
\8c©";
445 str = dungeons[0].dname;
454 Strcpy(cvt_buf, str);
458 convert_line(in_line, out_line)
459 char *in_line, *out_line;
465 for (c = xcrypt(in_line, xbuf); *c; c++) {
477 /* insert "a"/"an" prefix */
479 Strcat(cc, An(cvt_buf));
483 Strcat(cc, an(cvt_buf));
490 cvt_buf[0] = highc(cvt_buf[0]);
494 /* replace name with pronoun;
495 valid for %d, %l, %n, and %o */
496 case 'h': /* he/she */
497 case 'H': /* He/She */
498 case 'i': /* him/her */
500 case 'j': /* his/her */
502 if (index("dlno", lowc(*(c - 1))))
503 qtext_pronoun(*(c - 1), *c);
505 --c; /* default action */
511 cvt_buf[0] = highc(cvt_buf[0]);
515 Strcpy(cvt_buf, makeplural(cvt_buf));
518 /* append possessive suffix */
521 cvt_buf[0] = highc(cvt_buf[0]);
526 Strcpy(cvt_buf, s_suffix(cvt_buf));
530 /* strip any "the" prefix */
533 if (!strncmpi(cvt_buf, "the ", 4)) {
534 Strcat(cc, &cvt_buf[4]);
542 --c; /* undo switch increment */
546 cc += strlen(cvt_buf);
548 } /* else fall through */
555 if (cc > &out_line[BUFSZ-1])
556 panic("convert_line: overflow");
562 deliver_by_pline(qt_msg)
563 struct qtmsg *qt_msg;
566 char in_line[BUFSZ], out_line[BUFSZ];
569 for (size = 0; size < qt_msg->size; size += (long) strlen(in_line)) {
570 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
571 convert_line(in_line, out_line);
572 pline("%s", out_line);
577 deliver_by_window(qt_msg, how)
578 struct qtmsg *qt_msg;
582 char in_line[BUFSZ], out_line[BUFSZ];
583 boolean qtdump = (how == NHW_MAP);
584 winid datawin = create_nhwindow(qtdump ? NHW_TEXT : how);
590 /* when dumping quest messages at startup, all of them are passed to
591 * deliver_by_window(), even if normally given to deliver_by_pline()
593 Sprintf(buf, "msgnum: %d, delivery: %c",
594 qt_msg->msgnum, qt_msg->delivery);
595 putstr(datawin, 0, buf);
596 putstr(datawin, 0, "");
599 for (size = 0; size < qt_msg->size; size += (long) strlen(in_line)) {
600 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
601 convert_line(in_line, out_line);
602 putstr(datawin, 0, out_line);
604 display_nhwindow(datawin, TRUE);
605 destroy_nhwindow(datawin);
607 /* block messages delivered by window aren't kept in message history
608 but have a one-line summary which is put there for ^P recall */
610 if (qt_msg->summary_size) {
611 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
612 convert_line(in_line, out_line);
613 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
614 } else if (qt_msg->delivery == 'c') { /* skip for 'qtdump' of 'p' */
615 /* delivery 'c' and !summary_size, summary expected but not present;
616 this doesn't prefix the number with role code vs 'general'
617 but should be good enough for summary verification purposes */
618 Sprintf(out_line, "[missing block message summary for #%05d]",
623 putmsghistory(out_line, FALSE);
630 /* WIZKIT: suppress plot feedback if starting with quest artifact */
631 if (program_state.wizkit_wishing)
633 if (!(common ? qt_list.common : qt_list.chrole)) {
634 panic("%s: no %s quest text data available",
635 common ? "com_pager" : "qt_pager",
636 common ? "common" : "role-specific");
647 struct qtmsg *qt_msg;
649 if (skip_pager(TRUE))
652 if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
653 impossible("com_pager: message %d not found.", msgnum);
657 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
658 if (qt_msg->delivery == 'p')
659 deliver_by_pline(qt_msg);
660 else if (msgnum == 1)
661 deliver_by_window(qt_msg, NHW_MENU);
663 deliver_by_window(qt_msg, NHW_TEXT);
671 struct qtmsg *qt_msg;
673 if (skip_pager(FALSE))
676 qt_msg = msg_in(qt_list.chrole, msgnum);
678 /* some roles have an alternate message for return to the goal
679 level when the quest artifact is absent (handled by caller)
680 but some don't; for the latter, use the normal goal message;
681 note: for first visit, artifact is assumed to always be
682 present which might not be true for wizard mode but we don't
683 worry about quest message references in that situation */
684 if (msgnum == QT_ALTGOAL)
685 qt_msg = msg_in(qt_list.chrole, QT_NEXTGOAL);
688 impossible("qt_pager: message %d not found.", msgnum);
692 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
693 if (qt_msg->delivery == 'p' && strcmp(windowprocs.name, "X11"))
694 deliver_by_pline(qt_msg);
696 deliver_by_window(qt_msg, NHW_TEXT);
706 qpm = urole.enemy1num;
707 if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
709 return mkclass(urole.enemy1sym, 0);
711 qpm = urole.enemy2num;
712 if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
714 return mkclass(urole.enemy2sym, 0);
717 /* special levels can include a custom arrival message; display it */
719 deliver_splev_message()
721 char *str, *nl, in_line[BUFSZ], out_line[BUFSZ];
723 /* there's no provision for delivering via window instead of pline */
725 /* lev_message can span multiple lines using embedded newline chars;
726 any segments too long to fit within in_line[] will be truncated */
727 for (str = lev_message; *str; str = nl + 1) {
728 /* copying will stop at newline if one is present */
729 copynchars(in_line, str, (int) (sizeof in_line) - 1);
731 /* convert_line() expects encrypted input */
732 (void) xcrypt(in_line, in_line);
733 convert_line(in_line, out_line);
734 pline("%s", out_line);
736 if ((nl = index(str, '\n')) == 0)
737 break; /* done if no newline */
740 free((genericptr_t) lev_message);