OSDN Git Service

18656e5e71301c635ac2015c08ec54e68fa463a1
[jnethack/source.git] / src / rumors.c
1 /* NetHack 3.6  rumors.c        $NHDT-Date: 1446713640 2015/11/05 08:54:00 $  $NHDT-Branch: master $:$NHDT-Revision: 1.27 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "hack.h"
6 #include "lev.h"
7 #include "dlb.h"
8
9 /*      [note: this comment is fairly old, but still accurate for 3.1]
10  * Rumors have been entirely rewritten to speed up the access.  This is
11  * essential when working from floppies.  Using fseek() the way that's done
12  * here means rumors following longer rumors are output more often than those
13  * following shorter rumors.  Also, you may see the same rumor more than once
14  * in a particular game (although the odds are highly against it), but
15  * this also happens with real fortune cookies.  -dgk
16  */
17
18 /*      3.6
19  * The rumors file consists of a "do not edit" line, then a line containing
20  * three sets of three counts (first two in decimal, third in hexadecimal).
21  * The first set has the number of true rumors, the count in bytes for all
22  * true rumors, and the file offset to the first one.  The second set has
23  * the same group of numbers for the false rumors.  The third set has 0 for
24  * count, 0 for size, and the file offset for end-of-file.  The offset of
25  * the first true rumor plus the size of the true rumors matches the offset
26  * of the first false rumor.  Likewise, the offset of the first false rumor
27  * plus the size of the false rumors matches the offset for end-of-file.
28  */
29
30 /*      3.1     [now obsolete for rumors but still accurate for oracles]
31  * The rumors file consists of a "do not edit" line, a hexadecimal number
32  * giving the number of bytes of useful/true rumors, followed by those
33  * true rumors (one per line), followed by the useless/false/misleading/cute
34  * rumors (also one per line).  Number of bytes of untrue rumors is derived
35  * via fseek(EOF)+ftell().
36  *
37  * The oracles file consists of a "do not edit" comment, a decimal count N
38  * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
39  * records, separated by "---" lines.  The first oracle is a special case,
40  * and placed there by 'makedefs'.
41  */
42
43 STATIC_DCL void FDECL(init_rumors, (dlb *));
44 STATIC_DCL void FDECL(init_oracles, (dlb *));
45
46 /* rumor size variables are signed so that value -1 can be used as a flag */
47 static long true_rumor_size = 0L, false_rumor_size;
48 /* rumor start offsets are unsigned because they're handled via %lx format */
49 static unsigned long true_rumor_start, false_rumor_start;
50 /* rumor end offsets are signed because they're compared with [dlb_]ftell() */
51 static long true_rumor_end, false_rumor_end;
52 /* oracles are handled differently from rumors... */
53 static int oracle_flg = 0; /* -1=>don't use, 0=>need init, 1=>init done */
54 static unsigned oracle_cnt = 0;
55 static unsigned long *oracle_loc = 0;
56
57 STATIC_OVL void
58 init_rumors(fp)
59 dlb *fp;
60 {
61     static const char rumors_header[] = "%d,%ld,%lx;%d,%ld,%lx;0,0,%lx\n";
62     int true_count, false_count; /* in file but not used here */
63     unsigned long eof_offset;
64     char line[BUFSZ];
65
66     (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment */
67     (void) dlb_fgets(line, sizeof line, fp);
68     if (sscanf(line, rumors_header, &true_count, &true_rumor_size,
69                &true_rumor_start, &false_count, &false_rumor_size,
70                &false_rumor_start, &eof_offset) == 7
71         && true_rumor_size > 0L
72         && false_rumor_size > 0L) {
73         true_rumor_end = (long) true_rumor_start + true_rumor_size;
74         /* assert( true_rumor_end == false_rumor_start ); */
75         false_rumor_end = (long) false_rumor_start + false_rumor_size;
76         /* assert( false_rumor_end == eof_offset ); */
77     } else {
78         true_rumor_size = -1L; /* init failed */
79         (void) dlb_fclose(fp);
80     }
81 }
82
83 /* exclude_cookie is a hack used because we sometimes want to get rumors in a
84  * context where messages such as "You swallowed the fortune!" that refer to
85  * cookies should not appear.  This has no effect for true rumors since none
86  * of them contain such references anyway.
87  */
88 char *
89 getrumor(truth, rumor_buf, exclude_cookie)
90 int truth; /* 1=true, -1=false, 0=either */
91 char *rumor_buf;
92 boolean exclude_cookie;
93 {
94     dlb *rumors;
95     long tidbit, beginning;
96     char *endp, line[BUFSZ], xbuf[BUFSZ];
97
98     rumor_buf[0] = '\0';
99     if (true_rumor_size < 0L) /* we couldn't open RUMORFILE */
100         return rumor_buf;
101
102     rumors = dlb_fopen(RUMORFILE, "r");
103
104     if (rumors) {
105         int count = 0;
106         int adjtruth;
107
108         do {
109             rumor_buf[0] = '\0';
110             if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
111                 init_rumors(rumors);
112                 if (true_rumor_size < 0L) { /* init failed */
113                     Sprintf(rumor_buf, "Error reading \"%.80s\".", RUMORFILE);
114                     return rumor_buf;
115                 }
116             }
117             /*
118              *  input:      1    0   -1
119              *   rn2 \ +1  2=T  1=T  0=F
120              *   adj./ +0  1=T  0=F -1=F
121              */
122             switch (adjtruth = truth + rn2(2)) {
123             case 2: /*(might let a bogus input arg sneak thru)*/
124             case 1:
125                 beginning = (long) true_rumor_start;
126                 tidbit = Rand() % true_rumor_size;
127                 break;
128             case 0: /* once here, 0 => false rather than "either"*/
129             case -1:
130                 beginning = (long) false_rumor_start;
131                 tidbit = Rand() % false_rumor_size;
132                 break;
133             default:
134                 impossible("strange truth value for rumor");
135                 return strcpy(rumor_buf, "Oops...");
136             }
137             (void) dlb_fseek(rumors, beginning + tidbit, SEEK_SET);
138             (void) dlb_fgets(line, sizeof line, rumors);
139             if (!dlb_fgets(line, sizeof line, rumors)
140                 || (adjtruth > 0 && dlb_ftell(rumors) > true_rumor_end)) {
141                 /* reached end of rumors -- go back to beginning */
142                 (void) dlb_fseek(rumors, beginning, SEEK_SET);
143                 (void) dlb_fgets(line, sizeof line, rumors);
144             }
145             if ((endp = index(line, '\n')) != 0)
146                 *endp = 0;
147             Strcat(rumor_buf, xcrypt(line, xbuf));
148         } while (
149             count++ < 50 && exclude_cookie
150             && (strstri(rumor_buf, "fortune") || strstri(rumor_buf, "pity")));
151         (void) dlb_fclose(rumors);
152         if (count >= 50)
153             impossible("Can't find non-cookie rumor?");
154         else if (!in_mklev) /* avoid exercizing wisdom for graffiti */
155             exercise(A_WIS, (adjtruth > 0));
156     } else {
157         pline("Can't open rumors file!");
158         true_rumor_size = -1; /* don't try to open it again */
159     }
160 /* this is safe either way, so do it always since we can't get the definition
161  * out of makedefs.c
162  */
163 #define PAD_RUMORS_TO
164 #ifdef PAD_RUMORS_TO
165     /* remove padding */
166     {
167         char *x = eos(rumor_buf) - 1;
168
169         while (x > rumor_buf && *x == '_')
170             x--;
171         *++x = '\n';
172         *x = '\0';
173     }
174 #endif
175     return rumor_buf;
176 }
177
178 /*
179  * test that the true/false rumor boundaries are valid.
180  */
181 void
182 rumor_check()
183 {
184     dlb *rumors;
185     winid tmpwin;
186     char *endp, line[BUFSZ], xbuf[BUFSZ], rumor_buf[BUFSZ];
187
188     if (true_rumor_size < 0L) { /* we couldn't open RUMORFILE */
189     no_rumors:
190         pline("rumors not accessible.");
191         return;
192     }
193
194     rumors = dlb_fopen(RUMORFILE, "r");
195
196     if (rumors) {
197         long ftell_rumor_start = 0L;
198
199         rumor_buf[0] = '\0';
200         if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
201             init_rumors(rumors);
202             if (true_rumor_size < 0L)
203                 goto no_rumors; /* init failed */
204         }
205         tmpwin = create_nhwindow(NHW_TEXT);
206
207         /*
208          * reveal the values.
209          */
210
211         Sprintf(
212             rumor_buf,
213             "T start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
214             (long) true_rumor_start, true_rumor_start, true_rumor_end,
215             (unsigned long) true_rumor_end, true_rumor_size,
216             (unsigned long) true_rumor_size);
217         putstr(tmpwin, 0, rumor_buf);
218
219         Sprintf(
220             rumor_buf,
221             "F start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
222             (long) false_rumor_start, false_rumor_start, false_rumor_end,
223             (unsigned long) false_rumor_end, false_rumor_size,
224             (unsigned long) false_rumor_size);
225         putstr(tmpwin, 0, rumor_buf);
226
227         /*
228          * check the first rumor (start of true rumors) by
229          * skipping the first two lines.
230          *
231          * Then seek to the start of the false rumors (based on
232          * the value read in rumors, and display it.
233          */
234         rumor_buf[0] = '\0';
235         (void) dlb_fseek(rumors, (long) true_rumor_start, SEEK_SET);
236         ftell_rumor_start = dlb_ftell(rumors);
237         (void) dlb_fgets(line, sizeof line, rumors);
238         if ((endp = index(line, '\n')) != 0)
239             *endp = 0;
240         Sprintf(rumor_buf, "T %06ld %s", ftell_rumor_start,
241                 xcrypt(line, xbuf));
242         putstr(tmpwin, 0, rumor_buf);
243         /* find last true rumor */
244         while (dlb_fgets(line, sizeof line, rumors)
245                && dlb_ftell(rumors) < true_rumor_end)
246             continue;
247         if ((endp = index(line, '\n')) != 0)
248             *endp = 0;
249         Sprintf(rumor_buf, "  %6s %s", "", xcrypt(line, xbuf));
250         putstr(tmpwin, 0, rumor_buf);
251
252         rumor_buf[0] = '\0';
253         (void) dlb_fseek(rumors, (long) false_rumor_start, SEEK_SET);
254         ftell_rumor_start = dlb_ftell(rumors);
255         (void) dlb_fgets(line, sizeof line, rumors);
256         if ((endp = index(line, '\n')) != 0)
257             *endp = 0;
258         Sprintf(rumor_buf, "F %06ld %s", ftell_rumor_start,
259                 xcrypt(line, xbuf));
260         putstr(tmpwin, 0, rumor_buf);
261         /* find last false rumor */
262         while (dlb_fgets(line, sizeof line, rumors)
263                && dlb_ftell(rumors) < false_rumor_end)
264             continue;
265         if ((endp = index(line, '\n')) != 0)
266             *endp = 0;
267         Sprintf(rumor_buf, "  %6s %s", "", xcrypt(line, xbuf));
268         putstr(tmpwin, 0, rumor_buf);
269
270         (void) dlb_fclose(rumors);
271         display_nhwindow(tmpwin, TRUE);
272         destroy_nhwindow(tmpwin);
273     } else {
274         impossible("Can't open rumors file!");
275         true_rumor_size = -1; /* don't try to open it again */
276     }
277 }
278
279 /* Gets a random line of text from file 'fname', and returns it. */
280 char *
281 get_rnd_text(fname, buf)
282 const char *fname;
283 char *buf;
284 {
285     dlb *fh;
286
287     buf[0] = '\0';
288
289     fh = dlb_fopen(fname, "r");
290
291     if (fh) {
292         /* TODO: cache sizetxt, starttxt, endtxt. maybe cache file contents?
293          */
294         long sizetxt = 0, starttxt = 0, endtxt = 0, tidbit = 0;
295         char *endp, line[BUFSZ], xbuf[BUFSZ];
296         (void) dlb_fgets(line, sizeof line,
297                          fh); /* skip "don't edit" comment */
298
299         (void) dlb_fseek(fh, 0L, SEEK_CUR);
300         starttxt = dlb_ftell(fh);
301         (void) dlb_fseek(fh, 0L, SEEK_END);
302         endtxt = dlb_ftell(fh);
303         sizetxt = endtxt - starttxt;
304         tidbit = Rand() % sizetxt;
305
306         (void) dlb_fseek(fh, starttxt + tidbit, SEEK_SET);
307         (void) dlb_fgets(line, sizeof line, fh);
308         if (!dlb_fgets(line, sizeof line, fh)) {
309             (void) dlb_fseek(fh, starttxt, SEEK_SET);
310             (void) dlb_fgets(line, sizeof line, fh);
311         }
312         if ((endp = index(line, '\n')) != 0)
313             *endp = 0;
314         Strcat(buf, xcrypt(line, xbuf));
315         (void) dlb_fclose(fh);
316     } else
317         impossible("Can't open file %s!", fname);
318     return buf;
319 }
320
321 void
322 outrumor(truth, mechanism)
323 int truth; /* 1=true, -1=false, 0=either */
324 int mechanism;
325 {
326     static const char fortune_msg[] =
327         "This cookie has a scrap of paper inside.";
328     const char *line;
329     char buf[BUFSZ];
330     boolean reading = (mechanism == BY_COOKIE || mechanism == BY_PAPER);
331
332     if (reading) {
333         /* deal with various things that prevent reading */
334         if (is_fainted() && mechanism == BY_COOKIE)
335             return;
336         else if (Blind) {
337             if (mechanism == BY_COOKIE)
338                 pline(fortune_msg);
339             pline("What a pity that you cannot read it!");
340             return;
341         }
342     }
343     line = getrumor(truth, buf, reading ? FALSE : TRUE);
344     if (!*line)
345         line = "NetHack rumors file closed for renovation.";
346     switch (mechanism) {
347     case BY_ORACLE:
348         /* Oracle delivers the rumor */
349         pline("True to her word, the Oracle %ssays: ",
350               (!rn2(4) ? "offhandedly "
351                        : (!rn2(3) ? "casually "
352                                   : (rn2(2) ? "nonchalantly " : ""))));
353         verbalize1(line);
354         /* [WIS exercized by getrumor()] */
355         return;
356     case BY_COOKIE:
357         pline(fortune_msg);
358     /* FALLTHRU */
359     case BY_PAPER:
360         pline("It reads:");
361         break;
362     }
363     pline1(line);
364 }
365
366 STATIC_OVL void
367 init_oracles(fp)
368 dlb *fp;
369 {
370     register int i;
371     char line[BUFSZ];
372     int cnt = 0;
373
374     /* this assumes we're only called once */
375     (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/
376     (void) dlb_fgets(line, sizeof line, fp);
377     if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) {
378         oracle_cnt = (unsigned) cnt;
379         oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long));
380         for (i = 0; i < cnt; i++) {
381             (void) dlb_fgets(line, sizeof line, fp);
382             (void) sscanf(line, "%5lx\n", &oracle_loc[i]);
383         }
384     }
385     return;
386 }
387
388 void
389 save_oracles(fd, mode)
390 int fd, mode;
391 {
392     if (perform_bwrite(mode)) {
393         bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
394         if (oracle_cnt)
395             bwrite(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof(long));
396     }
397     if (release_data(mode)) {
398         if (oracle_cnt) {
399             free((genericptr_t) oracle_loc);
400             oracle_loc = 0, oracle_cnt = 0, oracle_flg = 0;
401         }
402     }
403 }
404
405 void
406 restore_oracles(fd)
407 int fd;
408 {
409     mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
410     if (oracle_cnt) {
411         oracle_loc = (unsigned long *) alloc(oracle_cnt * sizeof(long));
412         mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof(long));
413         oracle_flg = 1; /* no need to call init_oracles() */
414     }
415 }
416
417 void
418 outoracle(special, delphi)
419 boolean special;
420 boolean delphi;
421 {
422     char line[COLNO];
423     char *endp;
424     dlb *oracles;
425     int oracle_idx;
426     char xbuf[BUFSZ];
427
428     /* early return if we couldn't open ORACLEFILE on previous attempt,
429        or if all the oracularities are already exhausted */
430     if (oracle_flg < 0 || (oracle_flg > 0 && oracle_cnt == 0))
431         return;
432
433     oracles = dlb_fopen(ORACLEFILE, "r");
434
435     if (oracles) {
436         winid tmpwin;
437         if (oracle_flg == 0) { /* if this is the first outoracle() */
438             init_oracles(oracles);
439             oracle_flg = 1;
440             if (oracle_cnt == 0)
441                 return;
442         }
443         /* oracle_loc[0] is the special oracle;
444            oracle_loc[1..oracle_cnt-1] are normal ones */
445         if (oracle_cnt <= 1 && !special)
446             return; /*(shouldn't happen)*/
447         oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
448         (void) dlb_fseek(oracles, (long) oracle_loc[oracle_idx], SEEK_SET);
449         if (!special) /* move offset of very last one into this slot */
450             oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];
451
452         tmpwin = create_nhwindow(NHW_TEXT);
453         if (delphi)
454             putstr(tmpwin, 0,
455                    special
456                      ? "The Oracle scornfully takes all your money and says:"
457                      : "The Oracle meditates for a moment and then intones:");
458         else
459             putstr(tmpwin, 0, "The message reads:");
460         putstr(tmpwin, 0, "");
461
462         while (dlb_fgets(line, COLNO, oracles) && strcmp(line, "---\n")) {
463             if ((endp = index(line, '\n')) != 0)
464                 *endp = 0;
465             putstr(tmpwin, 0, xcrypt(line, xbuf));
466         }
467         display_nhwindow(tmpwin, TRUE);
468         destroy_nhwindow(tmpwin);
469         (void) dlb_fclose(oracles);
470     } else {
471         pline("Can't open oracles file!");
472         oracle_flg = -1; /* don't try to open it again */
473     }
474 }
475
476 int
477 doconsult(oracl)
478 struct monst *oracl;
479 {
480     long umoney;
481     int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
482     int add_xpts;
483     char qbuf[QBUFSZ];
484
485     multi = 0;
486     umoney = money_cnt(invent);
487
488     if (!oracl) {
489         There("is no one here to consult.");
490         return 0;
491     } else if (!oracl->mpeaceful) {
492         pline("%s is in no mood for consultations.", Monnam(oracl));
493         return 0;
494     } else if (!umoney) {
495         You("have no money.");
496         return 0;
497     }
498
499     Sprintf(qbuf, "\"Wilt thou settle for a minor consultation?\" (%d %s)",
500             minor_cost, currency((long) minor_cost));
501     switch (ynq(qbuf)) {
502     default:
503     case 'q':
504         return 0;
505     case 'y':
506         if (umoney < (long) minor_cost) {
507             You("don't even have enough money for that!");
508             return 0;
509         }
510         u_pay = minor_cost;
511         break;
512     case 'n':
513         if (umoney <= (long) minor_cost /* don't even ask */
514             || (oracle_cnt == 1 || oracle_flg < 0))
515             return 0;
516         Sprintf(qbuf, "\"Then dost thou desire a major one?\" (%d %s)",
517                 major_cost, currency((long) major_cost));
518         if (yn(qbuf) != 'y')
519             return 0;
520         u_pay = (umoney < (long) major_cost) ? (int) umoney : major_cost;
521         break;
522     }
523     money2mon(oracl, (long) u_pay);
524     context.botl = 1;
525     add_xpts = 0; /* first oracle of each type gives experience points */
526     if (u_pay == minor_cost) {
527         outrumor(1, BY_ORACLE);
528         if (!u.uevent.minor_oracle)
529             add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
530         /* 5 pts if very 1st, or 2 pts if major already done */
531         u.uevent.minor_oracle = TRUE;
532     } else {
533         boolean cheapskate = u_pay < major_cost;
534
535         outoracle(cheapskate, TRUE);
536         if (!cheapskate && !u.uevent.major_oracle)
537             add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
538         /* ~100 pts if very 1st, ~40 pts if minor already done */
539         u.uevent.major_oracle = TRUE;
540         exercise(A_WIS, !cheapskate);
541     }
542     if (add_xpts) {
543         more_experienced(add_xpts, u_pay / 50);
544         newexplevel();
545     }
546     return 1;
547 }
548
549 /*rumors.c*/