OSDN Git Service

fix #36659
[jnethack/source.git] / src / questpgr.c
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. */
4
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. */
9
10 #include "hack.h"
11 #include "dlb.h"
12
13 /*  quest-specific pager routines. */
14
15 #include "qtext.h"
16
17 #define QTEXT_FILE "quest.dat"
18
19 #ifdef TTY_GRAPHICS
20 #include "wintty.h"
21 #endif
22
23 /* from sp_lev.c, for deliver_splev_message() */
24 extern char *lev_message;
25
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));
40
41 static char cvt_buf[64];
42 static struct qtlists qt_list;
43 static dlb *msg_file;
44 /* used by ldrname() and neminame(), then copied into cvt_buf */
45 static char nambuf[sizeof cvt_buf];
46
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 */
50 static void
51 dump_qtlist()
52 {
53 #ifdef DEBUG
54     struct qtmsg *msg;
55
56     if (!explicitdebug(__FILE__))
57         return;
58
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);
62     }
63 #endif /* DEBUG */
64     return;
65 }
66
67 static void
68 Fread(ptr, size, nitems, stream)
69 genericptr_t ptr;
70 int size, nitems;
71 dlb *stream;
72 {
73     int cnt;
74
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));
78     }
79 }
80
81 STATIC_OVL struct qtmsg *
82 construct_qtlist(hdr_offset)
83 long hdr_offset;
84 {
85     struct qtmsg *msg_list;
86     int n_msgs;
87
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));
92
93     /*
94      * Load up the list.
95      */
96     Fread((genericptr_t) msg_list, n_msgs * sizeof (struct qtmsg), 1,
97           msg_file);
98
99     msg_list[n_msgs].msgnum = -1;
100     return msg_list;
101 }
102
103 void
104 load_qtlist()
105 {
106     int n_classes, i;
107     char qt_classes[N_HDR][LEN_HDR];
108     long qt_offsets[N_HDR];
109
110     msg_file = dlb_fopen(QTEXT_FILE, RDBMODE);
111     if (!msg_file)
112         panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE);
113
114     /*
115      * Read in the number of classes, then the ID's & offsets for
116      * each header.
117      */
118
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);
122
123     /*
124      * Now construct the message lists for quick reference later
125      * on when we are actually paging the messages out.
126      */
127
128     qt_list.common = qt_list.chrole = (struct qtmsg *) 0;
129
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]);
138 #endif
139     }
140
141     if (!qt_list.common || !qt_list.chrole)
142         impossible("load_qtlist: cannot load quest text.");
143     dump_qtlist();
144     return; /* no ***DON'T*** close the msg_file */
145 }
146
147 /* called at program exit */
148 void
149 unload_qtlist()
150 {
151     if (msg_file)
152         (void) dlb_fclose(msg_file), msg_file = 0;
153     if (qt_list.common)
154         free((genericptr_t) qt_list.common), qt_list.common = 0;
155     if (qt_list.chrole)
156         free((genericptr_t) qt_list.chrole), qt_list.chrole = 0;
157     return;
158 }
159
160 short
161 quest_info(typ)
162 int typ;
163 {
164     switch (typ) {
165     case 0:
166         return urole.questarti;
167     case MS_LEADER:
168         return urole.ldrnum;
169     case MS_NEMESIS:
170         return urole.neminum;
171     case MS_GUARDIAN:
172         return urole.guardnum;
173     default:
174         impossible("quest_info(%d)", typ);
175     }
176     return 0;
177 }
178
179 /* return your role leader's name */
180 const char *
181 ldrname()
182 {
183     int i = urole.ldrnum;
184
185 #if 0 /*JP*/
186     Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
187             mons[i].mname);
188 #else
189     Strcpy(nambuf, mons[i].mname);
190 #endif
191     return nambuf;
192 }
193
194 /* return your intermediate target string */
195 STATIC_OVL const char *
196 intermed()
197 {
198     return urole.intermed;
199 }
200
201 boolean
202 is_quest_artifact(otmp)
203 struct obj *otmp;
204 {
205     return (boolean) (otmp->oartifact == urole.questarti);
206 }
207
208 /* return your role nemesis' name */
209 STATIC_OVL const char *
210 neminame()
211 {
212     int i = urole.neminum;
213
214 #if 0 /*JP*/
215     Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
216             mons[i].mname);
217 #else
218     Strcpy(nambuf, mons[i].mname);
219 #endif
220     return nambuf;
221 }
222
223 STATIC_OVL const char *
224 guardname() /* return your role leader's guard monster name */
225 {
226     int i = urole.guardnum;
227
228     return mons[i].mname;
229 }
230
231 STATIC_OVL const char *
232 homebase() /* return your role leader's location */
233 {
234     return urole.homebase;
235 }
236
237 /* replace deity, leader, nemesis, or artifact name with pronoun;
238    overwrites cvt_buf[] */
239 STATIC_OVL void
240 qtext_pronoun(who, which)
241 char who,  /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => artifact */
242     which; /* 'h'|'H'|'i'|'I'|'j'|'J' */
243 {
244     const char *pnoun;
245     int g;
246     char lwhich = lowc(which); /* H,I,J -> h,i,j */
247
248     /*
249      * Invalid subject (not d,l,n,o) yields neuter, singular result.
250      *
251      * For %o, treat all artifacts as neuter; some have plural names,
252      * which genders[] doesn't handle; cvt_buf[] already contains name.
253      */
254     if (who == 'o'
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" : "?";
260     } else {
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 : "?";
268     }
269     Strcpy(cvt_buf, pnoun);
270 #if 0 /*JP*/
271     /* capitalize for H,I,J */
272     if (lwhich != which)
273         cvt_buf[0] = highc(cvt_buf[0]);
274 #endif
275     return;
276 }
277
278 STATIC_OVL struct qtmsg *
279 msg_in(qtm_list, msgnum)
280 struct qtmsg *qtm_list;
281 int msgnum;
282 {
283     struct qtmsg *qt_msg;
284
285     for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++)
286         if (qt_msg->msgnum == msgnum)
287             return qt_msg;
288
289     return (struct qtmsg *) 0;
290 }
291
292 STATIC_OVL void
293 convert_arg(c)
294 char c;
295 {
296     register const char *str;
297
298     switch (c) {
299     case 'p':
300         str = plname;
301         break;
302     case 'c':
303         str = (flags.female && urole.name.f) ? urole.name.f : urole.name.m;
304         break;
305     case 'r':
306         str = rank_of(u.ulevel, Role_switch, flags.female);
307         break;
308     case 'R':
309         str = rank_of(MIN_QUEST_LEVEL, Role_switch, flags.female);
310         break;
311     case 's':
312 /*JP
313         str = (flags.female) ? "sister" : "brother";
314 */
315         str = (flags.female) ? "\96\85" : "\92í";
316         break;
317     case 'S':
318 /*JP
319         str = (flags.female) ? "daughter" : "son";
320 */
321         str = (flags.female) ? "\96º" : "\91§\8eq";
322         break;
323     case 'l':
324         str = ldrname();
325         break;
326     case 'i':
327         str = intermed();
328         break;
329     case 'O':
330     case 'o':
331         str = the(artiname(urole.questarti));
332 #if 0 /*JP*/
333         if (c == 'O') {
334             /* shorten "the Foo of Bar" to "the Foo"
335                (buffer returned by the() is modifiable) */
336             char *p = strstri(str, " of ");
337
338             if (p)
339                 *p = '\0';
340         }
341 #endif
342         break;
343     case 'n':
344         str = neminame();
345         break;
346     case 'g':
347         str = guardname();
348         break;
349     case 'G':
350         str = align_gtitle(u.ualignbase[A_ORIGINAL]);
351         break;
352     case 'H':
353         str = homebase();
354         break;
355     case 'a':
356         str = align_str(u.ualignbase[A_ORIGINAL]);
357         break;
358     case 'A':
359         str = align_str(u.ualign.type);
360         break;
361     case 'd':
362         str = align_gname(u.ualignbase[A_ORIGINAL]);
363         break;
364     case 'D':
365         str = align_gname(A_LAWFUL);
366         break;
367     case 'C':
368 /*JP
369         str = "chaotic";
370 */
371         str = "\8d¬\93×";
372         break;
373     case 'N':
374 /*JP
375         str = "neutral";
376 */
377         str = "\92\86\97§";
378         break;
379     case 'L':
380 /*JP
381         str = "lawful";
382 */
383         str = "\92\81\8f\98";
384         break;
385     case 'x':
386 /*JP
387         str = Blind ? "sense" : "see";
388 */
389         str = Blind ? "\8a´\82¶" : "\8c©";
390         break;
391     case 'Z':
392         str = dungeons[0].dname;
393         break;
394     case '%':
395         str = "%";
396         break;
397     default:
398         str = "";
399         break;
400     }
401     Strcpy(cvt_buf, str);
402 }
403
404 STATIC_OVL void
405 convert_line(in_line, out_line)
406 char *in_line, *out_line;
407 {
408     char *c, *cc;
409     char xbuf[BUFSZ];
410
411     cc = out_line;
412     for (c = xcrypt(in_line, xbuf); *c; c++) {
413         *cc = 0;
414         switch (*c) {
415         case '\r':
416         case '\n':
417             *(++cc) = 0;
418             return;
419
420         case '%':
421             if (*(c + 1)) {
422                 convert_arg(*(++c));
423                 switch (*(++c)) {
424                 /* insert "a"/"an" prefix */
425                 case 'A':
426                     Strcat(cc, An(cvt_buf));
427                     cc += strlen(cc);
428                     continue; /* for */
429                 case 'a':
430                     Strcat(cc, an(cvt_buf));
431                     cc += strlen(cc);
432                     continue; /* for */
433
434                 /* capitalize */
435                 case 'C':
436 #if 0 /*JP*/
437                     cvt_buf[0] = highc(cvt_buf[0]);
438 #endif
439                     break;
440
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 */
446                 case 'I':
447                 case 'j': /* his/her */
448                 case 'J':
449                     if (index("dlno", lowc(*(c - 1))))
450                         qtext_pronoun(*(c - 1), *c);
451                     else
452                         --c; /* default action */
453                     break;
454
455                 /* pluralize */
456                 case 'P':
457 #if 0 /*JP*/
458                     cvt_buf[0] = highc(cvt_buf[0]);
459 #endif
460                 case 'p':
461                     Strcpy(cvt_buf, makeplural(cvt_buf));
462                     break;
463
464                 /* append possessive suffix */
465                 case 'S':
466 #if 0 /*JP*/
467                     cvt_buf[0] = highc(cvt_buf[0]);
468 #endif
469                 case 's':
470                     Strcpy(cvt_buf, s_suffix(cvt_buf));
471                     break;
472
473                 /* strip any "the" prefix */
474                 case 't':
475 #if 0 /*JP*/
476                     if (!strncmpi(cvt_buf, "the ", 4)) {
477                         Strcat(cc, &cvt_buf[4]);
478                         cc += strlen(cc);
479                         continue; /* for */
480                     }
481 #endif
482                     break;
483
484                 default:
485                     --c; /* undo switch increment */
486                     break;
487                 }
488                 Strcat(cc, cvt_buf);
489                 cc += strlen(cvt_buf);
490                 break;
491             } /* else fall through */
492
493         default:
494             *cc++ = *c;
495             break;
496         }
497     }
498     if (cc > &out_line[BUFSZ-1])
499         panic("convert_line: overflow");
500     *cc = 0;
501     return;
502 }
503
504 STATIC_OVL void
505 deliver_by_pline(qt_msg)
506 struct qtmsg *qt_msg;
507 {
508     long size;
509     char in_line[BUFSZ], out_line[BUFSZ];
510
511     *in_line = '\0';
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);
516     }
517 }
518
519 STATIC_OVL void
520 deliver_by_window(qt_msg, how)
521 struct qtmsg *qt_msg;
522 int how;
523 {
524     long size;
525     char in_line[BUFSZ], out_line[BUFSZ];
526     boolean qtdump = (how == NHW_MAP);
527     winid datawin = create_nhwindow(qtdump ? NHW_TEXT : how);
528
529 #ifdef DEBUG
530     if (qtdump) {
531         char buf[BUFSZ];
532
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()
535          */
536         Sprintf(buf, "msgnum: %d, delivery: %c",
537                 qt_msg->msgnum, qt_msg->delivery);
538         putstr(datawin, 0, buf);
539         putstr(datawin, 0, "");
540     }
541 #endif
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);
546     }
547     display_nhwindow(datawin, TRUE);
548     destroy_nhwindow(datawin);
549
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 */
552     *out_line = '\0';
553     if (qt_msg->summary_size) {
554         (void) dlb_fgets(in_line, sizeof in_line, msg_file);
555         convert_line(in_line, out_line);
556 #ifdef BETA
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]",
562                 qt_msg->msgnum);
563 #endif
564     }
565     if (*out_line)
566         putmsghistory(out_line, FALSE);
567 }
568
569 boolean
570 skip_pager(common)
571 boolean common;
572 {
573     /* WIZKIT: suppress plot feedback if starting with quest artifact */
574     if (program_state.wizkit_wishing)
575         return TRUE;
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");
580         /*NOTREACHED*/
581         return TRUE;
582     }
583     return FALSE;
584 }
585
586 void
587 com_pager(msgnum)
588 int msgnum;
589 {
590     struct qtmsg *qt_msg;
591
592     if (skip_pager(TRUE))
593         return;
594
595     if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
596         impossible("com_pager: message %d not found.", msgnum);
597         return;
598     }
599
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);
605     else
606         deliver_by_window(qt_msg, NHW_TEXT);
607     return;
608 }
609
610 void
611 qt_pager(msgnum)
612 int msgnum;
613 {
614     struct qtmsg *qt_msg;
615
616     if (skip_pager(FALSE))
617         return;
618
619     if (!(qt_msg = msg_in(qt_list.chrole, msgnum))) {
620         impossible("qt_pager: message %d not found.", msgnum);
621         return;
622     }
623
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);
627     else
628         deliver_by_window(qt_msg, NHW_TEXT);
629     return;
630 }
631
632 struct permonst *
633 qt_montype()
634 {
635     int qpm;
636
637     if (rn2(5)) {
638         qpm = urole.enemy1num;
639         if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
640             return &mons[qpm];
641         return mkclass(urole.enemy1sym, 0);
642     }
643     qpm = urole.enemy2num;
644     if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
645         return &mons[qpm];
646     return mkclass(urole.enemy2sym, 0);
647 }
648
649 /* special levels can include a custom arrival message; display it */
650 void
651 deliver_splev_message()
652 {
653     char *str, *nl, in_line[BUFSZ], out_line[BUFSZ];
654
655     /* there's no provision for delivering via window instead of pline */
656     if (lev_message) {
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);
662
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);
667
668             if ((nl = index(str, '\n')) == 0)
669                 break; /* done if no newline */
670         }
671
672         free((genericptr_t) lev_message);
673         lev_message = 0;
674     }
675 }
676
677 /*questpgr.c*/