1 /* NetHack 3.6 rumors.c $NHDT-Date: 1583445339 2020/03/05 21:55:39 $ $NHDT-Branch: NetHack-3.6-Mar2020 $:$NHDT-Revision: 1.38 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
9 /* JNetHack may be freely redistributed. See license for details. */
15 /* [note: this comment is fairly old, but still accurate for 3.1]
16 * Rumors have been entirely rewritten to speed up the access. This is
17 * essential when working from floppies. Using fseek() the way that's done
18 * here means rumors following longer rumors are output more often than those
19 * following shorter rumors. Also, you may see the same rumor more than once
20 * in a particular game (although the odds are highly against it), but
21 * this also happens with real fortune cookies. -dgk
25 * The rumors file consists of a "do not edit" line, then a line containing
26 * three sets of three counts (first two in decimal, third in hexadecimal).
27 * The first set has the number of true rumors, the count in bytes for all
28 * true rumors, and the file offset to the first one. The second set has
29 * the same group of numbers for the false rumors. The third set has 0 for
30 * count, 0 for size, and the file offset for end-of-file. The offset of
31 * the first true rumor plus the size of the true rumors matches the offset
32 * of the first false rumor. Likewise, the offset of the first false rumor
33 * plus the size of the false rumors matches the offset for end-of-file.
36 /* 3.1 [now obsolete for rumors but still accurate for oracles]
37 * The rumors file consists of a "do not edit" line, a hexadecimal number
38 * giving the number of bytes of useful/true rumors, followed by those
39 * true rumors (one per line), followed by the useless/false/misleading/cute
40 * rumors (also one per line). Number of bytes of untrue rumors is derived
41 * via fseek(EOF)+ftell().
43 * The oracles file consists of a "do not edit" comment, a decimal count N
44 * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
45 * records, separated by "---" lines. The first oracle is a special case,
46 * and placed there by 'makedefs'.
49 STATIC_DCL void FDECL(init_rumors, (dlb *));
50 STATIC_DCL void FDECL(init_oracles, (dlb *));
51 STATIC_DCL void FDECL(couldnt_open_file, (const char *));
53 /* rumor size variables are signed so that value -1 can be used as a flag */
54 static long true_rumor_size = 0L, false_rumor_size;
55 /* rumor start offsets are unsigned because they're handled via %lx format */
56 static unsigned long true_rumor_start, false_rumor_start;
57 /* rumor end offsets are signed because they're compared with [dlb_]ftell() */
58 static long true_rumor_end, false_rumor_end;
59 /* oracles are handled differently from rumors... */
60 static int oracle_flg = 0; /* -1=>don't use, 0=>need init, 1=>init done */
61 static unsigned oracle_cnt = 0;
62 static unsigned long *oracle_loc = 0;
68 static const char rumors_header[] = "%d,%ld,%lx;%d,%ld,%lx;0,0,%lx\n";
69 int true_count, false_count; /* in file but not used here */
70 unsigned long eof_offset;
73 (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment */
74 (void) dlb_fgets(line, sizeof line, fp);
75 if (sscanf(line, rumors_header, &true_count, &true_rumor_size,
76 &true_rumor_start, &false_count, &false_rumor_size,
77 &false_rumor_start, &eof_offset) == 7
78 && true_rumor_size > 0L
79 && false_rumor_size > 0L) {
80 true_rumor_end = (long) true_rumor_start + true_rumor_size;
81 /* assert( true_rumor_end == false_rumor_start ); */
82 false_rumor_end = (long) false_rumor_start + false_rumor_size;
83 /* assert( false_rumor_end == eof_offset ); */
85 true_rumor_size = -1L; /* init failed */
86 (void) dlb_fclose(fp);
90 /* exclude_cookie is a hack used because we sometimes want to get rumors in a
91 * context where messages such as "You swallowed the fortune!" that refer to
92 * cookies should not appear. This has no effect for true rumors since none
93 * of them contain such references anyway.
96 getrumor(truth, rumor_buf, exclude_cookie)
97 int truth; /* 1=true, -1=false, 0=either */
99 boolean exclude_cookie;
102 long tidbit, beginning;
103 char *endp, line[BUFSZ], xbuf[BUFSZ];
106 if (true_rumor_size < 0L) /* we couldn't open RUMORFILE */
109 rumors = dlb_fopen(RUMORFILE, "r");
117 if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
119 if (true_rumor_size < 0L) { /* init failed */
120 Sprintf(rumor_buf, "Error reading \"%.80s\".", RUMORFILE);
126 * rn2 \ +1 2=T 1=T 0=F
127 * adj./ +0 1=T 0=F -1=F
129 switch (adjtruth = truth + rn2(2)) {
130 case 2: /*(might let a bogus input arg sneak thru)*/
132 beginning = (long) true_rumor_start;
133 tidbit = rn2(true_rumor_size);
135 case 0: /* once here, 0 => false rather than "either"*/
137 beginning = (long) false_rumor_start;
138 tidbit = rn2(false_rumor_size);
141 impossible("strange truth value for rumor");
142 return strcpy(rumor_buf, "Oops...");
144 (void) dlb_fseek(rumors, beginning + tidbit, SEEK_SET);
145 (void) dlb_fgets(line, sizeof line, rumors);
146 if (!dlb_fgets(line, sizeof line, rumors)
147 || (adjtruth > 0 && dlb_ftell(rumors) > true_rumor_end)) {
148 /* reached end of rumors -- go back to beginning */
149 (void) dlb_fseek(rumors, beginning, SEEK_SET);
150 (void) dlb_fgets(line, sizeof line, rumors);
152 if ((endp = index(line, '\n')) != 0)
154 Strcat(rumor_buf, xcrypt(line, xbuf));
156 count++ < 50 && exclude_cookie
158 && (strstri(rumor_buf, "fortune") || strstri(rumor_buf, "pity")));
160 && (strstri(rumor_buf, "
\90è") || strstri(rumor_buf, "
\82È
\82ñ
\82Ä
\82±
\82Æ
\82¾")));
162 (void) dlb_fclose(rumors);
164 impossible("Can't find non-cookie rumor?");
165 else if (!in_mklev) /* avoid exercizing wisdom for graffiti */
166 exercise(A_WIS, (adjtruth > 0));
168 couldnt_open_file(RUMORFILE);
169 true_rumor_size = -1; /* don't try to open it again */
172 /* this is safe either way, so do it always since we can't get the
173 * definition out of makedefs.c
175 #define PAD_RUMORS_TO
179 char *x = eos(rumor_buf) - 1;
181 while (x > rumor_buf && *x == '_')
191 * test that the true/false rumor boundaries are valid.
198 char *endp, line[BUFSZ], xbuf[BUFSZ], rumor_buf[BUFSZ];
200 if (true_rumor_size < 0L) { /* we couldn't open RUMORFILE */
202 pline("rumors not accessible.");
206 rumors = dlb_fopen(RUMORFILE, "r");
209 long ftell_rumor_start = 0L;
212 if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
214 if (true_rumor_size < 0L) {
215 rumors = (dlb *) 0; /* init_rumors() closes it upon failure */
216 goto no_rumors; /* init failed */
219 tmpwin = create_nhwindow(NHW_TEXT);
225 "T start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
226 (long) true_rumor_start, true_rumor_start,
227 true_rumor_end, (unsigned long) true_rumor_end,
228 true_rumor_size, (unsigned long) true_rumor_size);
229 putstr(tmpwin, 0, rumor_buf);
231 "F start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
232 (long) false_rumor_start, false_rumor_start,
233 false_rumor_end, (unsigned long) false_rumor_end,
234 false_rumor_size, (unsigned long) false_rumor_size);
235 putstr(tmpwin, 0, rumor_buf);
238 * check the first rumor (start of true rumors) by
239 * skipping the first two lines.
241 * Then seek to the start of the false rumors (based on
242 * the value read in rumors, and display it.
245 (void) dlb_fseek(rumors, (long) true_rumor_start, SEEK_SET);
246 ftell_rumor_start = dlb_ftell(rumors);
247 (void) dlb_fgets(line, sizeof line, rumors);
248 if ((endp = index(line, '\n')) != 0)
250 Sprintf(rumor_buf, "T %06ld %s", ftell_rumor_start,
252 putstr(tmpwin, 0, rumor_buf);
253 /* find last true rumor */
254 while (dlb_fgets(line, sizeof line, rumors)
255 && dlb_ftell(rumors) < true_rumor_end)
257 if ((endp = index(line, '\n')) != 0)
259 Sprintf(rumor_buf, " %6s %s", "", xcrypt(line, xbuf));
260 putstr(tmpwin, 0, rumor_buf);
263 (void) dlb_fseek(rumors, (long) false_rumor_start, SEEK_SET);
264 ftell_rumor_start = dlb_ftell(rumors);
265 (void) dlb_fgets(line, sizeof line, rumors);
266 if ((endp = index(line, '\n')) != 0)
268 Sprintf(rumor_buf, "F %06ld %s", ftell_rumor_start,
270 putstr(tmpwin, 0, rumor_buf);
271 /* find last false rumor */
272 while (dlb_fgets(line, sizeof line, rumors)
273 && dlb_ftell(rumors) < false_rumor_end)
275 if ((endp = index(line, '\n')) != 0)
277 Sprintf(rumor_buf, " %6s %s", "", xcrypt(line, xbuf));
278 putstr(tmpwin, 0, rumor_buf);
280 (void) dlb_fclose(rumors);
281 display_nhwindow(tmpwin, TRUE);
282 destroy_nhwindow(tmpwin);
284 couldnt_open_file(RUMORFILE);
285 true_rumor_size = -1; /* don't try to open it again */
289 /* Gets a random line of text from file 'fname', and returns it.
290 rng is the random number generator to use, and should act like rn2 does. */
292 get_rnd_text(fname, buf, rng)
295 int FDECL((*rng), (int));
300 fh = dlb_fopen(fname, "r");
302 /* TODO: cache sizetxt, starttxt, endtxt. maybe cache file contents? */
303 long sizetxt = 0L, starttxt = 0L, endtxt = 0L, tidbit = 0L;
304 char *endp, line[BUFSZ], xbuf[BUFSZ];
306 /* skip "don't edit" comment */
307 (void) dlb_fgets(line, sizeof line, fh);
309 (void) dlb_fseek(fh, 0L, SEEK_CUR);
310 starttxt = dlb_ftell(fh);
311 (void) dlb_fseek(fh, 0L, SEEK_END);
312 endtxt = dlb_ftell(fh);
313 sizetxt = endtxt - starttxt;
314 /* might be zero (only if file is empty); should complain in that
315 case but if could happen over and over, also the suggestion
316 that save and restore might fix the problem wouldn't be useful */
319 tidbit = (*rng)(sizetxt);
321 (void) dlb_fseek(fh, starttxt + tidbit, SEEK_SET);
322 (void) dlb_fgets(line, sizeof line, fh);
323 if (!dlb_fgets(line, sizeof line, fh)) {
324 (void) dlb_fseek(fh, starttxt, SEEK_SET);
325 (void) dlb_fgets(line, sizeof line, fh);
327 if ((endp = index(line, '\n')) != 0)
329 Strcat(buf, xcrypt(line, xbuf));
330 (void) dlb_fclose(fh);
332 couldnt_open_file(fname);
339 outrumor(truth, mechanism)
340 int truth; /* 1=true, -1=false, 0=either */
343 static const char fortune_msg[] =
345 "This cookie has a scrap of paper inside.";
347 "
\82±
\82Ì
\83N
\83b
\83L
\81[
\82É
\82Í
\8e\86\90Ø
\82ª
\93ü
\82Á
\82Ä
\82¢
\82é
\81D";
350 boolean reading = (mechanism == BY_COOKIE || mechanism == BY_PAPER);
353 /* deal with various things that prevent reading */
354 if (is_fainted() && mechanism == BY_COOKIE)
357 if (mechanism == BY_COOKIE)
360 pline("What a pity that you cannot read it!");
362 pline("
\82»
\82ê
\82ð
\93Ç
\82ß
\82È
\82¢
\82È
\82ñ
\82Ä
\8bC
\82Ì
\93Å
\82È
\81I");
366 line = getrumor(truth, buf, reading ? FALSE : TRUE);
369 line = "NetHack rumors file closed for renovation.";
371 line = "
\89\
\82Ì
\90^
\91\8a\82Í
\8dü
\90V
\82Ì
\82½
\82ß
\8bx
\8a§
\82µ
\82Ä
\82¢
\82é
\81D";
374 /* Oracle delivers the rumor */
376 pline("True to her word, the Oracle %ssays: ",
377 (!rn2(4) ? "offhandedly "
378 : (!rn2(3) ? "casually "
379 : (rn2(2) ? "nonchalantly " : ""))));
381 pline("
\96ñ
\91©
\82Ç
\82¨
\82è
\82É
\81C
\8c«
\8eÒ
\82Í%s
\8fq
\82×
\82½:",
382 (!rn2(4) ? "
\96³
\91¢
\8dì
\82É"
383 : (!rn2(3) ? "
\89½
\8bC
\82È
\82"
384 : (rn2(2) ? "
\96³
\93Ú
\92\85\82É" : ""))));
387 /* [WIS exercized by getrumor()] */
396 pline("
\82»
\82ê
\82ð
\93Ç
\82ñ
\82¾:");
410 /* this assumes we're only called once */
411 (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/
412 (void) dlb_fgets(line, sizeof line, fp);
413 if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) {
414 oracle_cnt = (unsigned) cnt;
415 oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long));
416 for (i = 0; i < cnt; i++) {
417 (void) dlb_fgets(line, sizeof line, fp);
418 (void) sscanf(line, "%5lx\n", &oracle_loc[i]);
425 save_oracles(fd, mode)
428 if (perform_bwrite(mode)) {
429 bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
431 bwrite(fd, (genericptr_t) oracle_loc,
432 oracle_cnt * sizeof(long));
434 if (release_data(mode)) {
436 free((genericptr_t) oracle_loc);
437 oracle_loc = 0, oracle_cnt = 0, oracle_flg = 0;
446 mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
448 oracle_loc = (unsigned long *) alloc(oracle_cnt * sizeof(long));
449 mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof(long));
450 oracle_flg = 1; /* no need to call init_oracles() */
455 outoracle(special, delphi)
462 char *endp, line[COLNO], xbuf[BUFSZ];
464 /* early return if we couldn't open ORACLEFILE on previous attempt,
465 or if all the oracularities are already exhausted */
466 if (oracle_flg < 0 || (oracle_flg > 0 && oracle_cnt == 0))
469 oracles = dlb_fopen(ORACLEFILE, "r");
472 if (oracle_flg == 0) { /* if this is the first outoracle() */
473 init_oracles(oracles);
478 /* oracle_loc[0] is the special oracle;
479 oracle_loc[1..oracle_cnt-1] are normal ones */
480 if (oracle_cnt <= 1 && !special)
481 goto close_oracles; /*(shouldn't happen)*/
482 oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
483 (void) dlb_fseek(oracles, (long) oracle_loc[oracle_idx], SEEK_SET);
484 if (!special) /* move offset of very last one into this slot */
485 oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];
487 tmpwin = create_nhwindow(NHW_TEXT);
492 ? "The Oracle scornfully takes all your money and says:"
494 ? "
\8c«
\8eÒ
\82Í
\8cy
\95Ì
\82µ
\82½
\82æ
\82¤
\82É
\82 \82È
\82½
\82Ì
\91S
\82Ä
\82Ì
\82¨
\8bà
\82ð
\8eó
\82¯
\82Æ
\82è
\81C
\8fq
\82×
\82½
\81F"
496 : "The Oracle meditates for a moment and then intones:");
498 : "
\8c«
\8eÒ
\82Í
\82µ
\82Î
\82ç
\82áÒ
\91z
\82µ
\81C
\89Ì
\82¤
\82æ
\82¤
\82É
\98b
\82µ
\82½
\81F");
501 putstr(tmpwin, 0, "The message reads:");
503 putstr(tmpwin, 0, "
\83\81\83b
\83Z
\81[
\83W:");
504 putstr(tmpwin, 0, "");
506 while (dlb_fgets(line, COLNO, oracles) && strcmp(line, "---\n")) {
507 if ((endp = index(line, '\n')) != 0)
509 putstr(tmpwin, 0, xcrypt(line, xbuf));
511 display_nhwindow(tmpwin, TRUE);
512 destroy_nhwindow(tmpwin);
514 (void) dlb_fclose(oracles);
516 couldnt_open_file(ORACLEFILE);
517 oracle_flg = -1; /* don't try to open it again */
526 int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
531 umoney = money_cnt(invent);
535 There("is no one here to consult.");
537 pline("
\82±
\82±
\82É
\82Í
\90_
\91õ
\82ð
\8fq
\82×
\82é
\90l
\82Í
\82¢
\82È
\82¢
\81D");
539 } else if (!oracl->mpeaceful) {
541 pline("%s is in no mood for consultations.", Monnam(oracl));
543 pline("
\8c«
\8eÒ
\82Í
\90_
\91õ
\82ð
\8d\90\82°
\82Ä
\82
\82ê
\82é
\95µ
\88Í
\8bC
\82Å
\82Í
\82È
\82¢
\81D");
545 } else if (!umoney) {
547 You("have no money.");
549 You("
\82¨
\8bà
\82ª
\82È
\82¢
\81D");
554 Sprintf(qbuf, "\"Wilt thou settle for a minor consultation?\" (%d %s)",
556 Sprintf(qbuf, "
\81u
\93ð
\81C
\92á
\88Ê
\82Ì
\90_
\91õ
\82ð
\8eó
\82¯
\82é
\82©
\81H
\81v(%d%s)",
557 minor_cost, currency((long) minor_cost));
563 if (umoney < (long) minor_cost) {
565 You("don't even have enough money for that!");
567 You("
\82±
\82ê
\82É
\95¥
\82¦
\82é
\82¾
\82¯
\82Ì
\82¨
\8bà
\82·
\82ç
\8e\9d\82Á
\82Ä
\82¢
\82È
\82¢
\81I");
573 if (umoney <= (long) minor_cost /* don't even ask */
574 || (oracle_cnt == 1 || oracle_flg < 0))
577 Sprintf(qbuf, "\"Then dost thou desire a major one?\" (%d %s)",
579 Sprintf(qbuf, "
\81u
\82È
\82ç
\82Î
\93ð
\81C
\8d\82\88Ê
\82Ì
\90_
\91õ
\82ð
\8eó
\82¯
\82é
\82©
\81H
\81v(%d%s)",
580 major_cost, currency((long) major_cost));
583 u_pay = (umoney < (long) major_cost) ? (int) umoney : major_cost;
586 money2mon(oracl, (long) u_pay);
588 add_xpts = 0; /* first oracle of each type gives experience points */
589 if (u_pay == minor_cost) {
590 outrumor(1, BY_ORACLE);
591 if (!u.uevent.minor_oracle)
592 add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
593 /* 5 pts if very 1st, or 2 pts if major already done */
594 u.uevent.minor_oracle = TRUE;
596 boolean cheapskate = u_pay < major_cost;
598 outoracle(cheapskate, TRUE);
599 if (!cheapskate && !u.uevent.major_oracle)
600 add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
601 /* ~100 pts if very 1st, ~40 pts if minor already done */
602 u.uevent.major_oracle = TRUE;
603 exercise(A_WIS, !cheapskate);
606 more_experienced(add_xpts, u_pay / 50);
613 couldnt_open_file(filename)
614 const char *filename;
616 int save_something = program_state.something_worth_saving;
618 /* most likely the file is missing, so suppress impossible()'s
619 "saving and restoring might fix this" (unless the fuzzer,
620 which escalates impossible to panic, is running) */
621 if (!iflags.debug_fuzzer)
622 program_state.something_worth_saving = 0;
625 impossible("Can't open '%s' file.", filename);
627 impossible("'%s'
\83t
\83@
\83C
\83\8b\82ª
\8aJ
\82¯
\82È
\82¢
\81D", filename);
628 program_state.something_worth_saving = save_something;