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. */
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
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.
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().
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'.
43 STATIC_DCL
void FDECL(init_rumors
, (dlb
*));
44 STATIC_DCL
void FDECL(init_oracles
, (dlb
*));
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;
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
;
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 ); */
78 true_rumor_size
= -1L; /* init failed */
79 (void) dlb_fclose(fp
);
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.
89 getrumor(truth
, rumor_buf
, exclude_cookie
)
90 int truth
; /* 1=true, -1=false, 0=either */
92 boolean exclude_cookie
;
95 long tidbit
, beginning
;
96 char *endp
, line
[BUFSZ
], xbuf
[BUFSZ
];
99 if (true_rumor_size
< 0L) /* we couldn't open RUMORFILE */
102 rumors
= dlb_fopen(RUMORFILE
, "r");
110 if (true_rumor_size
== 0L) { /* if this is 1st outrumor() */
112 if (true_rumor_size
< 0L) { /* init failed */
113 Sprintf(rumor_buf
, "Error reading \"%.80s\".", RUMORFILE
);
119 * rn2 \ +1 2=T 1=T 0=F
120 * adj./ +0 1=T 0=F -1=F
122 switch (adjtruth
= truth
+ rn2(2)) {
123 case 2: /*(might let a bogus input arg sneak thru)*/
125 beginning
= (long) true_rumor_start
;
126 tidbit
= Rand() % true_rumor_size
;
128 case 0: /* once here, 0 => false rather than "either"*/
130 beginning
= (long) false_rumor_start
;
131 tidbit
= Rand() % false_rumor_size
;
134 impossible("strange truth value for rumor");
135 return strcpy(rumor_buf
, "Oops...");
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
);
145 if ((endp
= index(line
, '\n')) != 0)
147 Strcat(rumor_buf
, xcrypt(line
, xbuf
));
149 count
++ < 50 && exclude_cookie
150 && (strstri(rumor_buf
, "fortune") || strstri(rumor_buf
, "pity")));
151 (void) dlb_fclose(rumors
);
153 impossible("Can't find non-cookie rumor?");
154 else if (!in_mklev
) /* avoid exercizing wisdom for graffiti */
155 exercise(A_WIS
, (adjtruth
> 0));
157 pline("Can't open rumors file!");
158 true_rumor_size
= -1; /* don't try to open it again */
160 /* this is safe either way, so do it always since we can't get the definition
163 #define PAD_RUMORS_TO
167 char *x
= eos(rumor_buf
) - 1;
169 while (x
> rumor_buf
&& *x
== '_')
179 * test that the true/false rumor boundaries are valid.
186 char *endp
, line
[BUFSZ
], xbuf
[BUFSZ
], rumor_buf
[BUFSZ
];
188 if (true_rumor_size
< 0L) { /* we couldn't open RUMORFILE */
190 pline("rumors not accessible.");
194 rumors
= dlb_fopen(RUMORFILE
, "r");
197 long ftell_rumor_start
= 0L;
200 if (true_rumor_size
== 0L) { /* if this is 1st outrumor() */
202 if (true_rumor_size
< 0L)
203 goto no_rumors
; /* init failed */
205 tmpwin
= create_nhwindow(NHW_TEXT
);
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
);
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
);
228 * check the first rumor (start of true rumors) by
229 * skipping the first two lines.
231 * Then seek to the start of the false rumors (based on
232 * the value read in rumors, and display it.
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)
240 Sprintf(rumor_buf
, "T %06ld %s", ftell_rumor_start
,
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
)
247 if ((endp
= index(line
, '\n')) != 0)
249 Sprintf(rumor_buf
, " %6s %s", "", xcrypt(line
, xbuf
));
250 putstr(tmpwin
, 0, rumor_buf
);
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)
258 Sprintf(rumor_buf
, "F %06ld %s", ftell_rumor_start
,
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
)
265 if ((endp
= index(line
, '\n')) != 0)
267 Sprintf(rumor_buf
, " %6s %s", "", xcrypt(line
, xbuf
));
268 putstr(tmpwin
, 0, rumor_buf
);
270 (void) dlb_fclose(rumors
);
271 display_nhwindow(tmpwin
, TRUE
);
272 destroy_nhwindow(tmpwin
);
274 impossible("Can't open rumors file!");
275 true_rumor_size
= -1; /* don't try to open it again */
279 /* Gets a random line of text from file 'fname', and returns it. */
281 get_rnd_text(fname
, buf
)
289 fh
= dlb_fopen(fname
, "r");
292 /* TODO: cache sizetxt, starttxt, endtxt. maybe cache file contents?
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 */
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
;
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
);
312 if ((endp
= index(line
, '\n')) != 0)
314 Strcat(buf
, xcrypt(line
, xbuf
));
315 (void) dlb_fclose(fh
);
317 impossible("Can't open file %s!", fname
);
322 outrumor(truth
, mechanism
)
323 int truth
; /* 1=true, -1=false, 0=either */
326 static const char fortune_msg
[] =
327 "This cookie has a scrap of paper inside.";
330 boolean reading
= (mechanism
== BY_COOKIE
|| mechanism
== BY_PAPER
);
333 /* deal with various things that prevent reading */
334 if (is_fainted() && mechanism
== BY_COOKIE
)
337 if (mechanism
== BY_COOKIE
)
339 pline("What a pity that you cannot read it!");
343 line
= getrumor(truth
, buf
, reading
? FALSE
: TRUE
);
345 line
= "NetHack rumors file closed for renovation.";
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 " : ""))));
354 /* [WIS exercized by getrumor()] */
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
]);
389 save_oracles(fd
, mode
)
392 if (perform_bwrite(mode
)) {
393 bwrite(fd
, (genericptr_t
) &oracle_cnt
, sizeof oracle_cnt
);
395 bwrite(fd
, (genericptr_t
) oracle_loc
, oracle_cnt
* sizeof(long));
397 if (release_data(mode
)) {
399 free((genericptr_t
) oracle_loc
);
400 oracle_loc
= 0, oracle_cnt
= 0, oracle_flg
= 0;
409 mread(fd
, (genericptr_t
) &oracle_cnt
, sizeof 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() */
418 outoracle(special
, delphi
)
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))
433 oracles
= dlb_fopen(ORACLEFILE
, "r");
437 if (oracle_flg
== 0) { /* if this is the first outoracle() */
438 init_oracles(oracles
);
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
];
452 tmpwin
= create_nhwindow(NHW_TEXT
);
456 ? "The Oracle scornfully takes all your money and says:"
457 : "The Oracle meditates for a moment and then intones:");
459 putstr(tmpwin
, 0, "The message reads:");
460 putstr(tmpwin
, 0, "");
462 while (dlb_fgets(line
, COLNO
, oracles
) && strcmp(line
, "---\n")) {
463 if ((endp
= index(line
, '\n')) != 0)
465 putstr(tmpwin
, 0, xcrypt(line
, xbuf
));
467 display_nhwindow(tmpwin
, TRUE
);
468 destroy_nhwindow(tmpwin
);
469 (void) dlb_fclose(oracles
);
471 pline("Can't open oracles file!");
472 oracle_flg
= -1; /* don't try to open it again */
481 int u_pay
, minor_cost
= 50, major_cost
= 500 + 50 * u
.ulevel
;
486 umoney
= money_cnt(invent
);
489 There("is no one here to consult.");
491 } else if (!oracl
->mpeaceful
) {
492 pline("%s is in no mood for consultations.", Monnam(oracl
));
494 } else if (!umoney
) {
495 You("have no money.");
499 Sprintf(qbuf
, "\"Wilt thou settle for a minor consultation?\" (%d %s)",
500 minor_cost
, currency((long) minor_cost
));
506 if (umoney
< (long) minor_cost
) {
507 You("don't even have enough money for that!");
513 if (umoney
<= (long) minor_cost
/* don't even ask */
514 || (oracle_cnt
== 1 || oracle_flg
< 0))
516 Sprintf(qbuf
, "\"Then dost thou desire a major one?\" (%d %s)",
517 major_cost
, currency((long) major_cost
));
520 u_pay
= (umoney
< (long) major_cost
) ? (int) umoney
: major_cost
;
523 money2mon(oracl
, (long) u_pay
);
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
;
533 boolean cheapskate
= u_pay
< major_cost
;
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
);
543 more_experienced(add_xpts
, u_pay
/ 50);