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. */
8 /* quest-specific pager routines. */
12 #define QTEXT_FILE "quest.dat"
18 /* from sp_lev.c, for deliver_splev_message() */
19 extern char *lev_message
;
21 static void NDECL(dump_qtlist
);
22 static void FDECL(Fread
, (genericptr_t
, int, int, dlb
*));
23 STATIC_DCL
struct qtmsg
*FDECL(construct_qtlist
, (long));
24 STATIC_DCL
const char *NDECL(intermed
);
25 STATIC_DCL
const char *NDECL(neminame
);
26 STATIC_DCL
const char *NDECL(guardname
);
27 STATIC_DCL
const char *NDECL(homebase
);
28 STATIC_DCL
void FDECL(qtext_pronoun
, (CHAR_P
, CHAR_P
));
29 STATIC_DCL
struct qtmsg
*FDECL(msg_in
, (struct qtmsg
*, int));
30 STATIC_DCL
void FDECL(convert_arg
, (CHAR_P
));
31 STATIC_DCL
void FDECL(convert_line
, (char *,char *));
32 STATIC_DCL
void FDECL(deliver_by_pline
, (struct qtmsg
*));
33 STATIC_DCL
void FDECL(deliver_by_window
, (struct qtmsg
*, int));
34 STATIC_DCL boolean
FDECL(skip_pager
, (BOOLEAN_P
));
36 static char cvt_buf
[64];
37 static struct qtlists qt_list
;
39 /* used by ldrname() and neminame(), then copied into cvt_buf */
40 static char nambuf
[sizeof cvt_buf
];
42 /* dump the character msg list to check appearance;
43 build with DEBUG enabled and use DEBUGFILES=questpgr.c
44 in sysconf file or environment */
51 if (!explicitdebug(__FILE__
))
54 for (msg
= qt_list
.chrole
; msg
->msgnum
> 0; msg
++) {
55 (void) dlb_fseek(msg_file
, msg
->offset
, SEEK_SET
);
56 deliver_by_window(msg
, NHW_MAP
);
63 Fread(ptr
, size
, nitems
, stream
)
70 if ((cnt
= dlb_fread(ptr
, size
, nitems
, stream
)) != nitems
) {
71 panic("PREMATURE EOF ON QUEST TEXT FILE! Expected %d bytes, got %d",
72 (size
* nitems
), (size
* cnt
));
76 STATIC_OVL
struct qtmsg
*
77 construct_qtlist(hdr_offset
)
80 struct qtmsg
*msg_list
;
83 (void) dlb_fseek(msg_file
, hdr_offset
, SEEK_SET
);
84 Fread(&n_msgs
, sizeof(int), 1, msg_file
);
85 msg_list
= (struct qtmsg
*) alloc((unsigned) (n_msgs
+ 1)
86 * sizeof (struct qtmsg
));
91 Fread((genericptr_t
) msg_list
, n_msgs
* sizeof (struct qtmsg
), 1,
94 msg_list
[n_msgs
].msgnum
= -1;
102 char qt_classes
[N_HDR
][LEN_HDR
];
103 long qt_offsets
[N_HDR
];
105 msg_file
= dlb_fopen(QTEXT_FILE
, RDBMODE
);
107 panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE
);
110 * Read in the number of classes, then the ID's & offsets for
114 Fread(&n_classes
, sizeof (int), 1, msg_file
);
115 Fread(&qt_classes
[0][0], sizeof (char) * LEN_HDR
, n_classes
, msg_file
);
116 Fread(qt_offsets
, sizeof (long), n_classes
, msg_file
);
119 * Now construct the message lists for quick reference later
120 * on when we are actually paging the messages out.
123 qt_list
.common
= qt_list
.chrole
= (struct qtmsg
*) 0;
125 for (i
= 0; i
< n_classes
; i
++) {
126 if (!strncmp(COMMON_ID
, qt_classes
[i
], LEN_HDR
))
127 qt_list
.common
= construct_qtlist(qt_offsets
[i
]);
128 else if (!strncmp(urole
.filecode
, qt_classes
[i
], LEN_HDR
))
129 qt_list
.chrole
= construct_qtlist(qt_offsets
[i
]);
130 #if 0 /* UNUSED but available */
131 else if (!strncmp(urace
.filecode
, qt_classes
[i
], LEN_HDR
))
132 qt_list
.chrace
= construct_qtlist(qt_offsets
[i
]);
136 if (!qt_list
.common
|| !qt_list
.chrole
)
137 impossible("load_qtlist: cannot load quest text.");
139 return; /* no ***DON'T*** close the msg_file */
142 /* called at program exit */
147 (void) dlb_fclose(msg_file
), msg_file
= 0;
149 free((genericptr_t
) qt_list
.common
), qt_list
.common
= 0;
151 free((genericptr_t
) qt_list
.chrole
), qt_list
.chrole
= 0;
161 return urole
.questarti
;
165 return urole
.neminum
;
167 return urole
.guardnum
;
169 impossible("quest_info(%d)", typ
);
174 /* return your role leader's name */
178 int i
= urole
.ldrnum
;
180 Sprintf(nambuf
, "%s%s", type_is_pname(&mons
[i
]) ? "" : "the ",
185 /* return your intermediate target string */
186 STATIC_OVL
const char *
189 return urole
.intermed
;
193 is_quest_artifact(otmp
)
196 return (boolean
) (otmp
->oartifact
== urole
.questarti
);
199 /* return your role nemesis' name */
200 STATIC_OVL
const char *
203 int i
= urole
.neminum
;
205 Sprintf(nambuf
, "%s%s", type_is_pname(&mons
[i
]) ? "" : "the ",
210 STATIC_OVL
const char *
211 guardname() /* return your role leader's guard monster name */
213 int i
= urole
.guardnum
;
215 return mons
[i
].mname
;
218 STATIC_OVL
const char *
219 homebase() /* return your role leader's location */
221 return urole
.homebase
;
224 /* replace deity, leader, nemesis, or artifact name with pronoun;
225 overwrites cvt_buf[] */
227 qtext_pronoun(who
, which
)
228 char who
, /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => artifact */
229 which
; /* 'h'|'H'|'i'|'I'|'j'|'J' */
233 char lwhich
= lowc(which
); /* H,I,J -> h,i,j */
236 * Invalid subject (not d,l,n,o) yields neuter, singular result.
238 * For %o, treat all artifacts as neuter; some have plural names,
239 * which genders[] doesn't handle; cvt_buf[] already contains name.
242 && (strstri(cvt_buf
, "Eyes ")
243 || strcmpi(cvt_buf
, makesingular(cvt_buf
)))) {
244 pnoun
= (lwhich
== 'h') ? "they"
245 : (lwhich
== 'i') ? "them"
246 : (lwhich
== 'j') ? "their" : "?";
248 g
= (who
== 'd') ? quest_status
.godgend
249 : (who
== 'l') ? quest_status
.ldrgend
250 : (who
== 'n') ? quest_status
.nemgend
251 : 2; /* default to neuter */
252 pnoun
= (lwhich
== 'h') ? genders
[g
].he
253 : (lwhich
== 'i') ? genders
[g
].him
254 : (lwhich
== 'j') ? genders
[g
].his
: "?";
256 Strcpy(cvt_buf
, pnoun
);
257 /* capitalize for H,I,J */
259 cvt_buf
[0] = highc(cvt_buf
[0]);
263 STATIC_OVL
struct qtmsg
*
264 msg_in(qtm_list
, msgnum
)
265 struct qtmsg
*qtm_list
;
268 struct qtmsg
*qt_msg
;
270 for (qt_msg
= qtm_list
; qt_msg
->msgnum
> 0; qt_msg
++)
271 if (qt_msg
->msgnum
== msgnum
)
274 return (struct qtmsg
*) 0;
281 register const char *str
;
288 str
= (flags
.female
&& urole
.name
.f
) ? urole
.name
.f
: urole
.name
.m
;
291 str
= rank_of(u
.ulevel
, Role_switch
, flags
.female
);
294 str
= rank_of(MIN_QUEST_LEVEL
, Role_switch
, flags
.female
);
297 str
= (flags
.female
) ? "sister" : "brother";
300 str
= (flags
.female
) ? "daughter" : "son";
310 str
= the(artiname(urole
.questarti
));
312 /* shorten "the Foo of Bar" to "the Foo"
313 (buffer returned by the() is modifiable) */
314 char *p
= strstri(str
, " of ");
327 str
= align_gtitle(u
.ualignbase
[A_ORIGINAL
]);
333 str
= align_str(u
.ualignbase
[A_ORIGINAL
]);
336 str
= align_str(u
.ualign
.type
);
339 str
= align_gname(u
.ualignbase
[A_ORIGINAL
]);
342 str
= align_gname(A_LAWFUL
);
354 str
= Blind
? "sense" : "see";
357 str
= dungeons
[0].dname
;
366 Strcpy(cvt_buf
, str
);
370 convert_line(in_line
, out_line
)
371 char *in_line
, *out_line
;
377 for (c
= xcrypt(in_line
, xbuf
); *c
; c
++) {
389 /* insert "a"/"an" prefix */
391 Strcat(cc
, An(cvt_buf
));
395 Strcat(cc
, an(cvt_buf
));
401 cvt_buf
[0] = highc(cvt_buf
[0]);
404 /* replace name with pronoun;
405 valid for %d, %l, %n, and %o */
406 case 'h': /* he/she */
407 case 'H': /* He/She */
408 case 'i': /* him/her */
410 case 'j': /* his/her */
412 if (index("dlno", lowc(*(c
- 1))))
413 qtext_pronoun(*(c
- 1), *c
);
415 --c
; /* default action */
420 cvt_buf
[0] = highc(cvt_buf
[0]);
422 Strcpy(cvt_buf
, makeplural(cvt_buf
));
425 /* append possessive suffix */
427 cvt_buf
[0] = highc(cvt_buf
[0]);
429 Strcpy(cvt_buf
, s_suffix(cvt_buf
));
432 /* strip any "the" prefix */
434 if (!strncmpi(cvt_buf
, "the ", 4)) {
435 Strcat(cc
, &cvt_buf
[4]);
442 --c
; /* undo switch increment */
446 cc
+= strlen(cvt_buf
);
448 } /* else fall through */
455 if (cc
> &out_line
[BUFSZ
-1])
456 panic("convert_line: overflow");
462 deliver_by_pline(qt_msg
)
463 struct qtmsg
*qt_msg
;
466 char in_line
[BUFSZ
], out_line
[BUFSZ
];
469 for (size
= 0; size
< qt_msg
->size
; size
+= (long) strlen(in_line
)) {
470 (void) dlb_fgets(in_line
, sizeof in_line
, msg_file
);
471 convert_line(in_line
, out_line
);
472 pline("%s", out_line
);
477 deliver_by_window(qt_msg
, how
)
478 struct qtmsg
*qt_msg
;
482 char in_line
[BUFSZ
], out_line
[BUFSZ
];
483 boolean qtdump
= (how
== NHW_MAP
);
484 winid datawin
= create_nhwindow(qtdump
? NHW_TEXT
: how
);
490 /* when dumping quest messages at startup, all of them are passed to
491 * deliver_by_window(), even if normally given to deliver_by_pline()
493 Sprintf(buf
, "msgnum: %d, delivery: %c",
494 qt_msg
->msgnum
, qt_msg
->delivery
);
495 putstr(datawin
, 0, buf
);
496 putstr(datawin
, 0, "");
499 for (size
= 0; size
< qt_msg
->size
; size
+= (long) strlen(in_line
)) {
500 (void) dlb_fgets(in_line
, sizeof in_line
, msg_file
);
501 convert_line(in_line
, out_line
);
502 putstr(datawin
, 0, out_line
);
504 display_nhwindow(datawin
, TRUE
);
505 destroy_nhwindow(datawin
);
507 /* block messages delivered by window aren't kept in message history
508 but have a one-line summary which is put there for ^P recall */
510 if (qt_msg
->summary_size
) {
511 (void) dlb_fgets(in_line
, sizeof in_line
, msg_file
);
512 convert_line(in_line
, out_line
);
514 } else if (qt_msg
->delivery
== 'c') { /* skip for 'qtdump' of 'p' */
515 /* delivery 'c' and !summary_size, summary expected but not present;
516 this doesn't prefix the number with role code vs 'general'
517 but should be good enough for summary verification purposes */
518 Sprintf(out_line
, "[missing block message summary for #%05d]",
523 putmsghistory(out_line
, FALSE
);
530 /* WIZKIT: suppress plot feedback if starting with quest artifact */
531 if (program_state
.wizkit_wishing
)
533 if (!(common
? qt_list
.common
: qt_list
.chrole
)) {
534 panic("%s: no %s quest text data available",
535 common
? "com_pager" : "qt_pager",
536 common
? "common" : "role-specific");
547 struct qtmsg
*qt_msg
;
549 if (skip_pager(TRUE
))
552 if (!(qt_msg
= msg_in(qt_list
.common
, msgnum
))) {
553 impossible("com_pager: message %d not found.", msgnum
);
557 (void) dlb_fseek(msg_file
, qt_msg
->offset
, SEEK_SET
);
558 if (qt_msg
->delivery
== 'p')
559 deliver_by_pline(qt_msg
);
560 else if (msgnum
== 1)
561 deliver_by_window(qt_msg
, NHW_MENU
);
563 deliver_by_window(qt_msg
, NHW_TEXT
);
571 struct qtmsg
*qt_msg
;
573 if (skip_pager(FALSE
))
576 if (!(qt_msg
= msg_in(qt_list
.chrole
, msgnum
))) {
577 impossible("qt_pager: message %d not found.", msgnum
);
581 (void) dlb_fseek(msg_file
, qt_msg
->offset
, SEEK_SET
);
582 if (qt_msg
->delivery
== 'p' && strcmp(windowprocs
.name
, "X11"))
583 deliver_by_pline(qt_msg
);
585 deliver_by_window(qt_msg
, NHW_TEXT
);
595 qpm
= urole
.enemy1num
;
596 if (qpm
!= NON_PM
&& rn2(5) && !(mvitals
[qpm
].mvflags
& G_GENOD
))
598 return mkclass(urole
.enemy1sym
, 0);
600 qpm
= urole
.enemy2num
;
601 if (qpm
!= NON_PM
&& rn2(5) && !(mvitals
[qpm
].mvflags
& G_GENOD
))
603 return mkclass(urole
.enemy2sym
, 0);
606 /* special levels can include a custom arrival message; display it */
608 deliver_splev_message()
610 char *str
, *nl
, in_line
[BUFSZ
], out_line
[BUFSZ
];
612 /* there's no provision for delivering via window instead of pline */
614 /* lev_message can span multiple lines using embedded newline chars;
615 any segments too long to fit within in_line[] will be truncated */
616 for (str
= lev_message
; *str
; str
= nl
+ 1) {
617 /* copying will stop at newline if one is present */
618 copynchars(in_line
, str
, (int) (sizeof in_line
) - 1);
620 /* convert_line() expects encrypted input */
621 (void) xcrypt(in_line
, in_line
);
622 convert_line(in_line
, out_line
);
623 pline("%s", out_line
);
625 if ((nl
= index(str
, '\n')) == 0)
626 break; /* done if no newline */
629 free((genericptr_t
) lev_message
);