1 /* NetHack 3.6 questpgr.c $NHDT-Date: 1448541043 2015/11/26 12:30:43 $ $NHDT-Branch: master $:$NHDT-Revision: 1.36 $ */
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-2016 */
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 const char *NDECL(neminame);
31 STATIC_DCL const char *NDECL(guardname);
32 STATIC_DCL const char *NDECL(homebase);
33 STATIC_DCL void FDECL(qtext_pronoun, (CHAR_P, CHAR_P));
34 STATIC_DCL struct qtmsg *FDECL(msg_in, (struct qtmsg *, int));
35 STATIC_DCL void FDECL(convert_arg, (CHAR_P));
36 STATIC_DCL void FDECL(convert_line, (char *,char *));
37 STATIC_DCL void FDECL(deliver_by_pline, (struct qtmsg *));
38 STATIC_DCL void FDECL(deliver_by_window, (struct qtmsg *, int));
39 STATIC_DCL boolean FDECL(skip_pager, (BOOLEAN_P));
41 static char cvt_buf[64];
42 static struct qtlists qt_list;
44 /* used by ldrname() and neminame(), then copied into cvt_buf */
45 static char nambuf[sizeof cvt_buf];
47 /* dump the character msg list to check appearance;
48 build with DEBUG enabled and use DEBUGFILES=questpgr.c
49 in sysconf file or environment */
56 if (!explicitdebug(__FILE__))
59 for (msg = qt_list.chrole; msg->msgnum > 0; msg++) {
60 (void) dlb_fseek(msg_file, msg->offset, SEEK_SET);
61 deliver_by_window(msg, NHW_MAP);
68 Fread(ptr, size, nitems, stream)
75 if ((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) {
76 panic("PREMATURE EOF ON QUEST TEXT FILE! Expected %d bytes, got %d",
77 (size * nitems), (size * cnt));
81 STATIC_OVL struct qtmsg *
82 construct_qtlist(hdr_offset)
85 struct qtmsg *msg_list;
88 (void) dlb_fseek(msg_file, hdr_offset, SEEK_SET);
89 Fread(&n_msgs, sizeof(int), 1, msg_file);
90 msg_list = (struct qtmsg *) alloc((unsigned) (n_msgs + 1)
91 * sizeof (struct qtmsg));
96 Fread((genericptr_t) msg_list, n_msgs * sizeof (struct qtmsg), 1,
99 msg_list[n_msgs].msgnum = -1;
107 char qt_classes[N_HDR][LEN_HDR];
108 long qt_offsets[N_HDR];
110 msg_file = dlb_fopen(QTEXT_FILE, RDBMODE);
112 panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE);
115 * Read in the number of classes, then the ID's & offsets for
119 Fread(&n_classes, sizeof (int), 1, msg_file);
120 Fread(&qt_classes[0][0], sizeof (char) * LEN_HDR, n_classes, msg_file);
121 Fread(qt_offsets, sizeof (long), n_classes, msg_file);
124 * Now construct the message lists for quick reference later
125 * on when we are actually paging the messages out.
128 qt_list.common = qt_list.chrole = (struct qtmsg *) 0;
130 for (i = 0; i < n_classes; i++) {
131 if (!strncmp(COMMON_ID, qt_classes[i], LEN_HDR))
132 qt_list.common = construct_qtlist(qt_offsets[i]);
133 else if (!strncmp(urole.filecode, qt_classes[i], LEN_HDR))
134 qt_list.chrole = construct_qtlist(qt_offsets[i]);
135 #if 0 /* UNUSED but available */
136 else if (!strncmp(urace.filecode, qt_classes[i], LEN_HDR))
137 qt_list.chrace = construct_qtlist(qt_offsets[i]);
141 if (!qt_list.common || !qt_list.chrole)
142 impossible("load_qtlist: cannot load quest text.");
144 return; /* no ***DON'T*** close the msg_file */
147 /* called at program exit */
152 (void) dlb_fclose(msg_file), msg_file = 0;
154 free((genericptr_t) qt_list.common), qt_list.common = 0;
156 free((genericptr_t) qt_list.chrole), qt_list.chrole = 0;
166 return urole.questarti;
170 return urole.neminum;
172 return urole.guardnum;
174 impossible("quest_info(%d)", typ);
179 /* return your role leader's name */
183 int i = urole.ldrnum;
186 Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
189 Strcpy(nambuf, mons[i].mname);
194 /* return your intermediate target string */
195 STATIC_OVL const char *
198 return urole.intermed;
202 is_quest_artifact(otmp)
205 return (boolean) (otmp->oartifact == urole.questarti);
208 /* return your role nemesis' name */
209 STATIC_OVL const char *
212 int i = urole.neminum;
215 Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
218 Strcpy(nambuf, mons[i].mname);
223 STATIC_OVL const char *
224 guardname() /* return your role leader's guard monster name */
226 int i = urole.guardnum;
228 return mons[i].mname;
231 STATIC_OVL const char *
232 homebase() /* return your role leader's location */
234 return urole.homebase;
237 /* replace deity, leader, nemesis, or artifact name with pronoun;
238 overwrites cvt_buf[] */
240 qtext_pronoun(who, which)
241 char who, /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => artifact */
242 which; /* 'h'|'H'|'i'|'I'|'j'|'J' */
246 char lwhich = lowc(which); /* H,I,J -> h,i,j */
249 * Invalid subject (not d,l,n,o) yields neuter, singular result.
251 * For %o, treat all artifacts as neuter; some have plural names,
252 * which genders[] doesn't handle; cvt_buf[] already contains name.
255 && (strstri(cvt_buf, "Eyes ")
256 || strcmpi(cvt_buf, makesingular(cvt_buf)))) {
257 pnoun = (lwhich == 'h') ? "they"
258 : (lwhich == 'i') ? "them"
259 : (lwhich == 'j') ? "their" : "?";
261 g = (who == 'd') ? quest_status.godgend
262 : (who == 'l') ? quest_status.ldrgend
263 : (who == 'n') ? quest_status.nemgend
264 : 2; /* default to neuter */
265 pnoun = (lwhich == 'h') ? genders[g].he
266 : (lwhich == 'i') ? genders[g].him
267 : (lwhich == 'j') ? genders[g].his : "?";
269 Strcpy(cvt_buf, pnoun);
271 /* capitalize for H,I,J */
273 cvt_buf[0] = highc(cvt_buf[0]);
278 STATIC_OVL struct qtmsg *
279 msg_in(qtm_list, msgnum)
280 struct qtmsg *qtm_list;
283 struct qtmsg *qt_msg;
285 for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++)
286 if (qt_msg->msgnum == msgnum)
289 return (struct qtmsg *) 0;
296 register const char *str;
303 str = (flags.female && urole.name.f) ? urole.name.f : urole.name.m;
306 str = rank_of(u.ulevel, Role_switch, flags.female);
309 str = rank_of(MIN_QUEST_LEVEL, Role_switch, flags.female);
313 str = (flags.female) ? "sister" : "brother";
315 str = (flags.female) ? "
\96\85" : "
\92Ã";
319 str = (flags.female) ? "daughter" : "son";
321 str = (flags.female) ? "
\96º" : "
\91§
\8eq";
331 str = the(artiname(urole.questarti));
334 /* shorten "the Foo of Bar" to "the Foo"
335 (buffer returned by the() is modifiable) */
336 char *p = strstri(str, " of ");
350 str = align_gtitle(u.ualignbase[A_ORIGINAL]);
356 str = align_str(u.ualignbase[A_ORIGINAL]);
359 str = align_str(u.ualign.type);
362 str = align_gname(u.ualignbase[A_ORIGINAL]);
365 str = align_gname(A_LAWFUL);
383 str = "
\92\81\8f\98";
387 str = Blind ? "sense" : "see";
389 str = Blind ? "
\8a´
\82¶" : "
\8c©";
392 str = dungeons[0].dname;
401 Strcpy(cvt_buf, str);
405 convert_line(in_line, out_line)
406 char *in_line, *out_line;
412 for (c = xcrypt(in_line, xbuf); *c; c++) {
424 /* insert "a"/"an" prefix */
426 Strcat(cc, An(cvt_buf));
430 Strcat(cc, an(cvt_buf));
437 cvt_buf[0] = highc(cvt_buf[0]);
441 /* replace name with pronoun;
442 valid for %d, %l, %n, and %o */
443 case 'h': /* he/she */
444 case 'H': /* He/She */
445 case 'i': /* him/her */
447 case 'j': /* his/her */
449 if (index("dlno", lowc(*(c - 1))))
450 qtext_pronoun(*(c - 1), *c);
452 --c; /* default action */
458 cvt_buf[0] = highc(cvt_buf[0]);
461 Strcpy(cvt_buf, makeplural(cvt_buf));
464 /* append possessive suffix */
467 cvt_buf[0] = highc(cvt_buf[0]);
470 Strcpy(cvt_buf, s_suffix(cvt_buf));
473 /* strip any "the" prefix */
476 if (!strncmpi(cvt_buf, "the ", 4)) {
477 Strcat(cc, &cvt_buf[4]);
485 --c; /* undo switch increment */
489 cc += strlen(cvt_buf);
491 } /* else fall through */
498 if (cc > &out_line[BUFSZ-1])
499 panic("convert_line: overflow");
505 deliver_by_pline(qt_msg)
506 struct qtmsg *qt_msg;
509 char in_line[BUFSZ], out_line[BUFSZ];
512 for (size = 0; size < qt_msg->size; size += (long) strlen(in_line)) {
513 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
514 convert_line(in_line, out_line);
515 pline("%s", out_line);
520 deliver_by_window(qt_msg, how)
521 struct qtmsg *qt_msg;
525 char in_line[BUFSZ], out_line[BUFSZ];
526 boolean qtdump = (how == NHW_MAP);
527 winid datawin = create_nhwindow(qtdump ? NHW_TEXT : how);
533 /* when dumping quest messages at startup, all of them are passed to
534 * deliver_by_window(), even if normally given to deliver_by_pline()
536 Sprintf(buf, "msgnum: %d, delivery: %c",
537 qt_msg->msgnum, qt_msg->delivery);
538 putstr(datawin, 0, buf);
539 putstr(datawin, 0, "");
542 for (size = 0; size < qt_msg->size; size += (long) strlen(in_line)) {
543 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
544 convert_line(in_line, out_line);
545 putstr(datawin, 0, out_line);
547 display_nhwindow(datawin, TRUE);
548 destroy_nhwindow(datawin);
550 /* block messages delivered by window aren't kept in message history
551 but have a one-line summary which is put there for ^P recall */
553 if (qt_msg->summary_size) {
554 (void) dlb_fgets(in_line, sizeof in_line, msg_file);
555 convert_line(in_line, out_line);
557 } else if (qt_msg->delivery == 'c') { /* skip for 'qtdump' of 'p' */
558 /* delivery 'c' and !summary_size, summary expected but not present;
559 this doesn't prefix the number with role code vs 'general'
560 but should be good enough for summary verification purposes */
561 Sprintf(out_line, "[missing block message summary for #%05d]",
566 putmsghistory(out_line, FALSE);
573 /* WIZKIT: suppress plot feedback if starting with quest artifact */
574 if (program_state.wizkit_wishing)
576 if (!(common ? qt_list.common : qt_list.chrole)) {
577 panic("%s: no %s quest text data available",
578 common ? "com_pager" : "qt_pager",
579 common ? "common" : "role-specific");
590 struct qtmsg *qt_msg;
592 if (skip_pager(TRUE))
595 if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
596 impossible("com_pager: message %d not found.", msgnum);
600 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
601 if (qt_msg->delivery == 'p')
602 deliver_by_pline(qt_msg);
603 else if (msgnum == 1)
604 deliver_by_window(qt_msg, NHW_MENU);
606 deliver_by_window(qt_msg, NHW_TEXT);
614 struct qtmsg *qt_msg;
616 if (skip_pager(FALSE))
619 if (!(qt_msg = msg_in(qt_list.chrole, msgnum))) {
620 impossible("qt_pager: message %d not found.", msgnum);
624 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
625 if (qt_msg->delivery == 'p' && strcmp(windowprocs.name, "X11"))
626 deliver_by_pline(qt_msg);
628 deliver_by_window(qt_msg, NHW_TEXT);
638 qpm = urole.enemy1num;
639 if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
641 return mkclass(urole.enemy1sym, 0);
643 qpm = urole.enemy2num;
644 if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
646 return mkclass(urole.enemy2sym, 0);
649 /* special levels can include a custom arrival message; display it */
651 deliver_splev_message()
653 char *str, *nl, in_line[BUFSZ], out_line[BUFSZ];
655 /* there's no provision for delivering via window instead of pline */
657 /* lev_message can span multiple lines using embedded newline chars;
658 any segments too long to fit within in_line[] will be truncated */
659 for (str = lev_message; *str; str = nl + 1) {
660 /* copying will stop at newline if one is present */
661 copynchars(in_line, str, (int) (sizeof in_line) - 1);
663 /* convert_line() expects encrypted input */
664 (void) xcrypt(in_line, in_line);
665 convert_line(in_line, out_line);
666 pline("%s", out_line);
668 if ((nl = index(str, '\n')) == 0)
669 break; /* done if no newline */
672 free((genericptr_t) lev_message);