OSDN Git Service

update year to 2020
[jnethack/source.git] / src / questpgr.c
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. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
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 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));
41
42 static char cvt_buf[64];
43 static struct qtlists qt_list;
44 static dlb *msg_file;
45 /* used by ldrname() and neminame(), then copied into cvt_buf */
46 static char nambuf[sizeof cvt_buf];
47
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 */
51 static void
52 dump_qtlist()
53 {
54 #ifdef DEBUG
55     struct qtmsg *msg;
56
57     if (!explicitdebug(__FILE__))
58         return;
59
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);
63     }
64 #endif /* DEBUG */
65     return;
66 }
67
68 static void
69 Fread(ptr, size, nitems, stream)
70 genericptr_t ptr;
71 int size, nitems;
72 dlb *stream;
73 {
74     int cnt;
75
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));
79     }
80 }
81
82 STATIC_OVL struct qtmsg *
83 construct_qtlist(hdr_offset)
84 long hdr_offset;
85 {
86     struct qtmsg *msg_list;
87     int n_msgs;
88
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));
93
94     /*
95      * Load up the list.
96      */
97     Fread((genericptr_t) msg_list, n_msgs * sizeof (struct qtmsg), 1,
98           msg_file);
99
100     msg_list[n_msgs].msgnum = -1;
101     return msg_list;
102 }
103
104 void
105 load_qtlist()
106 {
107     int n_classes, i;
108     char qt_classes[N_HDR][LEN_HDR];
109     long qt_offsets[N_HDR];
110
111     msg_file = dlb_fopen(QTEXT_FILE, RDBMODE);
112     if (!msg_file)
113         panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE);
114
115     /*
116      * Read in the number of classes, then the ID's & offsets for
117      * each header.
118      */
119
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);
123
124     /*
125      * Now construct the message lists for quick reference later
126      * on when we are actually paging the messages out.
127      */
128
129     qt_list.common = qt_list.chrole = (struct qtmsg *) 0;
130
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]);
139 #endif
140     }
141
142     if (!qt_list.common || !qt_list.chrole)
143         impossible("load_qtlist: cannot load quest text.");
144     dump_qtlist();
145     return; /* no ***DON'T*** close the msg_file */
146 }
147
148 /* called at program exit */
149 void
150 unload_qtlist()
151 {
152     if (msg_file)
153         (void) dlb_fclose(msg_file), msg_file = 0;
154     if (qt_list.common)
155         free((genericptr_t) qt_list.common), qt_list.common = 0;
156     if (qt_list.chrole)
157         free((genericptr_t) qt_list.chrole), qt_list.chrole = 0;
158     return;
159 }
160
161 short
162 quest_info(typ)
163 int typ;
164 {
165     switch (typ) {
166     case 0:
167         return urole.questarti;
168     case MS_LEADER:
169         return urole.ldrnum;
170     case MS_NEMESIS:
171         return urole.neminum;
172     case MS_GUARDIAN:
173         return urole.guardnum;
174     default:
175         impossible("quest_info(%d)", typ);
176     }
177     return 0;
178 }
179
180 /* return your role leader's name */
181 const char *
182 ldrname()
183 {
184     int i = urole.ldrnum;
185
186 #if 0 /*JP*/
187     Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
188             mons[i].mname);
189 #else
190     Strcpy(nambuf, mons[i].mname);
191 #endif
192     return nambuf;
193 }
194
195 /* return your intermediate target string */
196 STATIC_OVL const char *
197 intermed()
198 {
199     return urole.intermed;
200 }
201
202 boolean
203 is_quest_artifact(otmp)
204 struct obj *otmp;
205 {
206     return (boolean) (otmp->oartifact == urole.questarti);
207 }
208
209 STATIC_OVL struct obj *
210 find_qarti(ochain)
211 struct obj *ochain;
212 {
213     struct obj *otmp, *qarti;
214
215     for (otmp = ochain; otmp; otmp = otmp->nobj) {
216         if (is_quest_artifact(otmp))
217             return otmp;
218         if (Has_contents(otmp) && (qarti = find_qarti(otmp->cobj)) != 0)
219             return qarti;
220     }
221     return (struct obj *) 0;
222 }
223
224 /* check several object chains for the quest artifact to determine
225    whether it is present on the current level */
226 struct obj *
227 find_quest_artifact(whichchains)
228 unsigned whichchains;
229 {
230     struct monst *mtmp;
231     struct obj *qarti = 0;
232
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))
240                 continue;
241             if ((qarti = find_qarti(mtmp->minvent)) != 0)
242                 break;
243         }
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))
248                 continue;
249             if ((qarti = find_qarti(mtmp->minvent)) != 0)
250                 break;
251         }
252         if (!qarti)
253             qarti = find_qarti(migrating_objs);
254     }
255     if (!qarti && (whichchains & (1 << OBJ_BURIED)) != 0)
256         qarti = find_qarti(level.buriedobjlist);
257
258     return qarti;
259 }
260
261 /* return your role nemesis' name */
262 STATIC_OVL const char *
263 neminame()
264 {
265     int i = urole.neminum;
266
267 #if 0 /*JP*/
268     Sprintf(nambuf, "%s%s", type_is_pname(&mons[i]) ? "" : "the ",
269             mons[i].mname);
270 #else
271     Strcpy(nambuf, mons[i].mname);
272 #endif
273     return nambuf;
274 }
275
276 STATIC_OVL const char *
277 guardname() /* return your role leader's guard monster name */
278 {
279     int i = urole.guardnum;
280
281     return mons[i].mname;
282 }
283
284 STATIC_OVL const char *
285 homebase() /* return your role leader's location */
286 {
287     return urole.homebase;
288 }
289
290 /* replace deity, leader, nemesis, or artifact name with pronoun;
291    overwrites cvt_buf[] */
292 STATIC_OVL void
293 qtext_pronoun(who, which)
294 char who,  /* 'd' => deity, 'l' => leader, 'n' => nemesis, 'o' => artifact */
295     which; /* 'h'|'H'|'i'|'I'|'j'|'J' */
296 {
297     const char *pnoun;
298     int g;
299     char lwhich = lowc(which); /* H,I,J -> h,i,j */
300
301     /*
302      * Invalid subject (not d,l,n,o) yields neuter, singular result.
303      *
304      * For %o, treat all artifacts as neuter; some have plural names,
305      * which genders[] doesn't handle; cvt_buf[] already contains name.
306      */
307     if (who == 'o'
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" : "?";
313     } else {
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 : "?";
321     }
322     Strcpy(cvt_buf, pnoun);
323 #if 0 /*JP*/
324     /* capitalize for H,I,J */
325     if (lwhich != which)
326         cvt_buf[0] = highc(cvt_buf[0]);
327 #endif
328     return;
329 }
330
331 STATIC_OVL struct qtmsg *
332 msg_in(qtm_list, msgnum)
333 struct qtmsg *qtm_list;
334 int msgnum;
335 {
336     struct qtmsg *qt_msg;
337
338     for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++)
339         if (qt_msg->msgnum == msgnum)
340             return qt_msg;
341
342     return (struct qtmsg *) 0;
343 }
344
345 STATIC_OVL void
346 convert_arg(c)
347 char c;
348 {
349     register const char *str;
350
351     switch (c) {
352     case 'p':
353         str = plname;
354         break;
355     case 'c':
356         str = (flags.female && urole.name.f) ? urole.name.f : urole.name.m;
357         break;
358     case 'r':
359         str = rank_of(u.ulevel, Role_switch, flags.female);
360         break;
361     case 'R':
362         str = rank_of(MIN_QUEST_LEVEL, Role_switch, flags.female);
363         break;
364     case 's':
365 /*JP
366         str = (flags.female) ? "sister" : "brother";
367 */
368         str = (flags.female) ? "\96\85" : "\92í";
369         break;
370     case 'S':
371 /*JP
372         str = (flags.female) ? "daughter" : "son";
373 */
374         str = (flags.female) ? "\96º" : "\91§\8eq";
375         break;
376     case 'l':
377         str = ldrname();
378         break;
379     case 'i':
380         str = intermed();
381         break;
382     case 'O':
383     case 'o':
384         str = the(artiname(urole.questarti));
385 #if 0 /*JP*/
386         if (c == 'O') {
387             /* shorten "the Foo of Bar" to "the Foo"
388                (buffer returned by the() is modifiable) */
389             char *p = strstri(str, " of ");
390
391             if (p)
392                 *p = '\0';
393         }
394 #endif
395         break;
396     case 'n':
397         str = neminame();
398         break;
399     case 'g':
400         str = guardname();
401         break;
402     case 'G':
403         str = align_gtitle(u.ualignbase[A_ORIGINAL]);
404         break;
405     case 'H':
406         str = homebase();
407         break;
408     case 'a':
409         str = align_str(u.ualignbase[A_ORIGINAL]);
410         break;
411     case 'A':
412         str = align_str(u.ualign.type);
413         break;
414     case 'd':
415         str = align_gname(u.ualignbase[A_ORIGINAL]);
416         break;
417     case 'D':
418         str = align_gname(A_LAWFUL);
419         break;
420     case 'C':
421 /*JP
422         str = "chaotic";
423 */
424         str = "\8d¬\93×";
425         break;
426     case 'N':
427 /*JP
428         str = "neutral";
429 */
430         str = "\92\86\97§";
431         break;
432     case 'L':
433 /*JP
434         str = "lawful";
435 */
436         str = "\92\81\8f\98";
437         break;
438     case 'x':
439 /*JP
440         str = Blind ? "sense" : "see";
441 */
442         str = Blind ? "\8a´\82¶" : "\8c©";
443         break;
444     case 'Z':
445         str = dungeons[0].dname;
446         break;
447     case '%':
448         str = "%";
449         break;
450     default:
451         str = "";
452         break;
453     }
454     Strcpy(cvt_buf, str);
455 }
456
457 STATIC_OVL void
458 convert_line(in_line, out_line)
459 char *in_line, *out_line;
460 {
461     char *c, *cc;
462     char xbuf[BUFSZ];
463
464     cc = out_line;
465     for (c = xcrypt(in_line, xbuf); *c; c++) {
466         *cc = 0;
467         switch (*c) {
468         case '\r':
469         case '\n':
470             *(++cc) = 0;
471             return;
472
473         case '%':
474             if (*(c + 1)) {
475                 convert_arg(*(++c));
476                 switch (*(++c)) {
477                 /* insert "a"/"an" prefix */
478                 case 'A':
479                     Strcat(cc, An(cvt_buf));
480                     cc += strlen(cc);
481                     continue; /* for */
482                 case 'a':
483                     Strcat(cc, an(cvt_buf));
484                     cc += strlen(cc);
485                     continue; /* for */
486
487                 /* capitalize */
488                 case 'C':
489 #if 0 /*JP*/
490                     cvt_buf[0] = highc(cvt_buf[0]);
491 #endif
492                     break;
493
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 */
499                 case 'I':
500                 case 'j': /* his/her */
501                 case 'J':
502                     if (index("dlno", lowc(*(c - 1))))
503                         qtext_pronoun(*(c - 1), *c);
504                     else
505                         --c; /* default action */
506                     break;
507
508                 /* pluralize */
509                 case 'P':
510 #if 0 /*JP*/
511                     cvt_buf[0] = highc(cvt_buf[0]);
512 #endif
513                     /*FALLTHRU*/
514                 case 'p':
515                     Strcpy(cvt_buf, makeplural(cvt_buf));
516                     break;
517
518                 /* append possessive suffix */
519                 case 'S':
520 #if 0 /*JP*/
521                     cvt_buf[0] = highc(cvt_buf[0]);
522 #endif
523                     /*FALLTHRU*/
524                 case 's':
525 #if 0 /*JP*/
526                     Strcpy(cvt_buf, s_suffix(cvt_buf));
527 #endif
528                     break;
529
530                 /* strip any "the" prefix */
531                 case 't':
532 #if 0 /*JP*/
533                     if (!strncmpi(cvt_buf, "the ", 4)) {
534                         Strcat(cc, &cvt_buf[4]);
535                         cc += strlen(cc);
536                         continue; /* for */
537                     }
538 #endif
539                     break;
540
541                 default:
542                     --c; /* undo switch increment */
543                     break;
544                 }
545                 Strcat(cc, cvt_buf);
546                 cc += strlen(cvt_buf);
547                 break;
548             } /* else fall through */
549
550         default:
551             *cc++ = *c;
552             break;
553         }
554     }
555     if (cc > &out_line[BUFSZ-1])
556         panic("convert_line: overflow");
557     *cc = 0;
558     return;
559 }
560
561 STATIC_OVL void
562 deliver_by_pline(qt_msg)
563 struct qtmsg *qt_msg;
564 {
565     long size;
566     char in_line[BUFSZ], out_line[BUFSZ];
567
568     *in_line = '\0';
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);
573     }
574 }
575
576 STATIC_OVL void
577 deliver_by_window(qt_msg, how)
578 struct qtmsg *qt_msg;
579 int how;
580 {
581     long size;
582     char in_line[BUFSZ], out_line[BUFSZ];
583     boolean qtdump = (how == NHW_MAP);
584     winid datawin = create_nhwindow(qtdump ? NHW_TEXT : how);
585
586 #ifdef DEBUG
587     if (qtdump) {
588         char buf[BUFSZ];
589
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()
592          */
593         Sprintf(buf, "msgnum: %d, delivery: %c",
594                 qt_msg->msgnum, qt_msg->delivery);
595         putstr(datawin, 0, buf);
596         putstr(datawin, 0, "");
597     }
598 #endif
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);
603     }
604     display_nhwindow(datawin, TRUE);
605     destroy_nhwindow(datawin);
606
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 */
609     *out_line = '\0';
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]",
619                 qt_msg->msgnum);
620 #endif
621     }
622     if (*out_line)
623         putmsghistory(out_line, FALSE);
624 }
625
626 STATIC_OVL boolean
627 skip_pager(common)
628 boolean common;
629 {
630     /* WIZKIT: suppress plot feedback if starting with quest artifact */
631     if (program_state.wizkit_wishing)
632         return TRUE;
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");
637         /*NOTREACHED*/
638         return TRUE;
639     }
640     return FALSE;
641 }
642
643 void
644 com_pager(msgnum)
645 int msgnum;
646 {
647     struct qtmsg *qt_msg;
648
649     if (skip_pager(TRUE))
650         return;
651
652     if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
653         impossible("com_pager: message %d not found.", msgnum);
654         return;
655     }
656
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);
662     else
663         deliver_by_window(qt_msg, NHW_TEXT);
664     return;
665 }
666
667 void
668 qt_pager(msgnum)
669 int msgnum;
670 {
671     struct qtmsg *qt_msg;
672
673     if (skip_pager(FALSE))
674         return;
675
676     qt_msg = msg_in(qt_list.chrole, msgnum);
677     if (!qt_msg) {
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);
686     }
687     if (!qt_msg) {
688         impossible("qt_pager: message %d not found.", msgnum);
689         return;
690     }
691
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);
695     else
696         deliver_by_window(qt_msg, NHW_TEXT);
697     return;
698 }
699
700 struct permonst *
701 qt_montype()
702 {
703     int qpm;
704
705     if (rn2(5)) {
706         qpm = urole.enemy1num;
707         if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
708             return &mons[qpm];
709         return mkclass(urole.enemy1sym, 0);
710     }
711     qpm = urole.enemy2num;
712     if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD))
713         return &mons[qpm];
714     return mkclass(urole.enemy2sym, 0);
715 }
716
717 /* special levels can include a custom arrival message; display it */
718 void
719 deliver_splev_message()
720 {
721     char *str, *nl, in_line[BUFSZ], out_line[BUFSZ];
722
723     /* there's no provision for delivering via window instead of pline */
724     if (lev_message) {
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);
730
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);
735
736             if ((nl = index(str, '\n')) == 0)
737                 break; /* done if no newline */
738         }
739
740         free((genericptr_t) lev_message);
741         lev_message = 0;
742     }
743 }
744
745 /*questpgr.c*/