1    | /*	SCCS Id: @(#)rumors.c	3.3	96/04/20	*/
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.1
19   |  * The rumors file consists of a "do not edit" line, a hexadecimal number
20   |  * giving the number of bytes of useful/true rumors, followed by those
21   |  * true rumors (one per line), followed by the useless/false/misleading/cute
22   |  * rumors (also one per line).  Number of bytes of untrue rumors is derived
23   |  * via fseek(EOF)+ftell().
24   |  *
25   |  * The oracles file consists of a "do not edit" comment, a decimal count N
26   |  * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
27   |  * records, separated by "---" lines.  The first oracle is a special case,
28   |  * and placed there by 'makedefs'.
29   |  */
30   | 
31   | STATIC_DCL void FDECL(init_rumors, (dlb *));
32   | STATIC_DCL void FDECL(init_oracles, (dlb *));
33   | 
34   | static long true_rumor_start,  true_rumor_size,  true_rumor_end,
35   | 	    false_rumor_start, false_rumor_size, false_rumor_end;
36   | static int oracle_flg = 0;  /* -1=>don't use, 0=>need init, 1=>init done */
37   | static unsigned oracle_cnt = 0;
38   | static long *oracle_loc = 0;
39   | 
40   | STATIC_OVL void
41   | init_rumors(fp)
42   | dlb *fp;
43   | {
44   | 	char line[BUFSZ];
45   | 
46   | 	(void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment */
47   | 	(void) dlb_fgets(line, sizeof line, fp);
48   | 	if (sscanf(line, "%6lx\n", &true_rumor_size) == 1 &&
49   | 	    true_rumor_size > 0L) {
50   | 	    (void) dlb_fseek(fp, 0L, SEEK_CUR);
51   | 	    true_rumor_start  = dlb_ftell(fp);
52   | 	    true_rumor_end    = true_rumor_start + true_rumor_size;
53   | 	    (void) dlb_fseek(fp, 0L, SEEK_END);
54   | 	    false_rumor_end   = dlb_ftell(fp);
55   | 	    false_rumor_start = true_rumor_end;	/* ok, so it's redundant... */
56   | 	    false_rumor_size  = false_rumor_end - false_rumor_start;
57   | 	} else
58   | 	    true_rumor_size = -1L;	/* init failed */
59   | }
60   | 
61   | /* exclude_cookie is a hack used because we sometimes want to get rumors in a
62   |  * context where messages such as "You swallowed the fortune!" that refer to
63   |  * cookies should not appear.  This has no effect for true rumors since none
64   |  * of them contain such references anyway.
65   |  */
66   | char *
67   | getrumor(truth, rumor_buf, exclude_cookie)
68   | int truth; /* 1=true, -1=false, 0=either */
69   | char *rumor_buf;
70   | boolean exclude_cookie; 
71   | {
72   | 	dlb	*rumors;
73   | 	long tidbit, beginning;
74   | 	char	*endp, line[BUFSZ], xbuf[BUFSZ];
75   | 
76   | 	rumor_buf[0] = '\0';
77   | 	if (true_rumor_size < 0L)	/* we couldn't open RUMORFILE */
78   | 		return rumor_buf;
79   | 
80   | 	rumors = dlb_fopen(RUMORFILE, "r");
81   | 
82   | 	if (rumors) {
83   | 	    int count = 0;
84   | 	    int adjtruth;
85   | 
86   | 	    do {
87   | 		rumor_buf[0] = '\0';
88   | 		if (true_rumor_size == 0L) {	/* if this is 1st outrumor() */
89   | 		    init_rumors(rumors);
90   | 		    if (true_rumor_size < 0L) {	/* init failed */
91   | 			Sprintf(rumor_buf, "Error reading \"%.80s\".",
92   | 				RUMORFILE);
93   | 			return rumor_buf;
94   | 		    }
95   | 		}
96   | 		/*
97   | 		 *	input:      1    0   -1
98   | 		 *	 rn2 \ +1  2=T  1=T  0=F
99   | 		 *	 adj./ +0  1=T  0=F -1=F
100  | 		 */
101  | 		switch (adjtruth = truth + rn2(2)) {
102  | 		  case  2:	/*(might let a bogus input arg sneak thru)*/
103  | 		  case  1:  beginning = true_rumor_start;
104  | 			    tidbit = Rand() % true_rumor_size;
105  | 			break;
106  | 		  case  0:	/* once here, 0 => false rather than "either"*/
107  | 		  case -1:  beginning = false_rumor_start;
108  | 			    tidbit = Rand() % false_rumor_size;
109  | 			break;
110  | 		  default:
111  | 			    impossible("strange truth value for rumor");
112  | 			return strcpy(rumor_buf, "Oops...");
113  | 		}
114  | 		(void) dlb_fseek(rumors, beginning + tidbit, SEEK_SET);
115  | 		(void) dlb_fgets(line, sizeof line, rumors);
116  | 		if (!dlb_fgets(line, sizeof line, rumors) ||
117  | 		    (adjtruth > 0 && dlb_ftell(rumors) > true_rumor_end)) {
118  | 			/* reached end of rumors -- go back to beginning */
119  | 			(void) dlb_fseek(rumors, beginning, SEEK_SET);
120  | 			(void) dlb_fgets(line, sizeof line, rumors);
121  | 		}
122  | 		if ((endp = index(line, '\n')) != 0) *endp = 0;
123  | 		Strcat(rumor_buf, xcrypt(line, xbuf));
124  | 	    } while(count++ < 50 && exclude_cookie && (strstri(rumor_buf, "fortune") || strstri(rumor_buf, "pity")));
125  | 	    (void) dlb_fclose(rumors);
126  | 	    if (count >= 50)
127  | 		impossible("Can't find non-cookie rumor?");
128  | 	    else
129  | 		exercise(A_WIS, (adjtruth > 0));
130  | 	} else {
131  | 		pline("Can't open rumors file!");
132  | 		true_rumor_size = -1;	/* don't try to open it again */
133  | 	}
134  | 	return rumor_buf;
135  | }
136  | 
137  | void
138  | outrumor(truth, mechanism)
139  | int truth; /* 1=true, -1=false, 0=either */
140  | int mechanism;
141  | {
142  | 	static const char fortune_msg[] =
143  | 		"This cookie has a scrap of paper inside.";
144  | 	const char *line;
145  | 	char buf[BUFSZ];
146  | 	boolean reading = (mechanism == BY_COOKIE ||
147  | 			   mechanism == BY_PAPER);
148  | 
149  | 	if (reading && Blind) {
150  | 		if (mechanism == BY_COOKIE)
151  | 			pline(fortune_msg);
152  | 		pline("What a pity that you cannot read it!");
153  | 		return;
154  | 	}
155  | 	line = getrumor(truth, buf, reading ? FALSE : TRUE);
156  | 	if (!*line)
157  | 		line = "NetHack rumors file closed for renovation.";
158  | 	switch (mechanism) {
159  | 	    case BY_ORACLE:
160  | 	 	/* Oracle delivers the rumor */
161  | 		pline("True to her word, the Oracle %ssays: ",
162  | 		  (!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " :
163  | 		  (rn2(2) ? "nonchalantly " : ""))));
164  | 		verbalize("%s", line);
165  | 		exercise(A_WIS, TRUE);
166  | 		return;
167  | 	    case BY_COOKIE:
168  | 		pline(fortune_msg);
169  | 		/* FALLTHRU */
170  | 	    case BY_PAPER:
171  | 		pline("It reads:");
172  | 		break;
173  | 	}
174  | 	pline("%s", line);
175  | }
176  | 
177  | STATIC_OVL void
178  | init_oracles(fp)
179  | dlb *fp;
180  | {
181  | 	register int i;
182  | 	char line[BUFSZ];
183  | 	int cnt = 0;
184  | 
185  | 	/* this assumes we're only called once */
186  | 	(void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/
187  | 	(void) dlb_fgets(line, sizeof line, fp);
188  | 	if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) {
189  | 	    oracle_cnt = (unsigned) cnt;
190  | 	    oracle_loc = (long *) alloc((unsigned)cnt * sizeof (long));
191  | 	    for (i = 0; i < cnt; i++) {
192  | 		(void) dlb_fgets(line, sizeof line, fp);
193  | 		(void) sscanf(line, "%5lx\n", &oracle_loc[i]);
194  | 	    }
195  | 	}
196  | 	return;
197  | }
198  | 
199  | void
200  | save_oracles(fd, mode)
201  | int fd, mode;
202  | {
203  | 	if (perform_bwrite(mode)) {
204  | 	    bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
205  | 	    if (oracle_cnt)
206  | 		bwrite(fd, (genericptr_t)oracle_loc, oracle_cnt*sizeof (long));
207  | 	}
208  | 	if (release_data(mode)) {
209  | 	    if (oracle_cnt) {
210  | 		free((genericptr_t)oracle_loc);
211  | 		oracle_loc = 0,  oracle_cnt = 0,  oracle_flg = 0;
212  | 	    }
213  | 	}
214  | }
215  | 
216  | void
217  | restore_oracles(fd)
218  | int fd;
219  | {
220  | 	mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
221  | 	if (oracle_cnt) {
222  | 	    oracle_loc = (long *) alloc(oracle_cnt * sizeof (long));
223  | 	    mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof (long));
224  | 	    oracle_flg = 1;	/* no need to call init_oracles() */
225  | 	}
226  | }
227  | 
228  | void
229  | outoracle(special, delphi)
230  | boolean special;
231  | boolean delphi;
232  | {
233  | 	char	line[COLNO];
234  | 	char	*endp;
235  | 	dlb	*oracles;
236  | 	int oracle_idx;
237  | 	char xbuf[BUFSZ];
238  | 
239  | 	if(oracle_flg < 0 ||			/* couldn't open ORACLEFILE */
240  | 	   (oracle_flg > 0 && oracle_cnt == 0))	/* oracles already exhausted */
241  | 		return;
242  | 
243  | 	oracles = dlb_fopen(ORACLEFILE, "r");
244  | 
245  | 	if (oracles) {
246  | 		winid tmpwin;
247  | 		if (oracle_flg == 0) {	/* if this is the first outoracle() */
248  | 			init_oracles(oracles);
249  | 			oracle_flg = 1;
250  | 			if (oracle_cnt == 0) return;
251  | 		}
252  | 		/* oracle_loc[0] is the special oracle;		*/
253  | 		/* oracle_loc[1..oracle_cnt-1] are normal ones	*/
254  | 		if (oracle_cnt <= 1 && !special) return;  /*(shouldn't happen)*/
255  | 		oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
256  | 		(void) dlb_fseek(oracles, oracle_loc[oracle_idx], SEEK_SET);
257  | 		if (!special) oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];
258  | 
259  | 		tmpwin = create_nhwindow(NHW_TEXT);
260  | 		if (delphi)
261  | 		    putstr(tmpwin, 0, special ?
262  | 		          "The Oracle scornfully takes all your money and says:" :
263  | 		          "The Oracle meditates for a moment and then intones:");
264  | 		else
265  | 		    putstr(tmpwin, 0, "The message reads:");
266  | 		putstr(tmpwin, 0, "");
267  | 
268  | 		while(dlb_fgets(line, COLNO, oracles) && strcmp(line,"---\n")) {
269  | 			if ((endp = index(line, '\n')) != 0) *endp = 0;
270  | 			putstr(tmpwin, 0, xcrypt(line, xbuf));
271  | 		}
272  | 		display_nhwindow(tmpwin, TRUE);
273  | 		destroy_nhwindow(tmpwin);
274  | 		(void) dlb_fclose(oracles);
275  | 	} else {
276  | 		pline("Can't open oracles file!");
277  | 		oracle_flg = -1;	/* don't try to open it again */
278  | 	}
279  | }
280  | 
281  | int
282  | doconsult(oracl)
283  | register struct monst *oracl;
284  | {
285  | 	int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
286  | 	int add_xpts;
287  | 	char qbuf[QBUFSZ];
288  | 
289  | 	multi = 0;
290  | 
291  | 	if (!oracl) {
292  | 		There("is no one here to consult.");
293  | 		return 0;
294  | 	} else if (!oracl->mpeaceful) {
295  | 		pline("%s is in no mood for consultations.", Monnam(oracl));
296  | 		return 0;
297  | 	} else if (!u.ugold) {
298  | 		You("have no money.");
299  | 		return 0;
300  | 	}
301  | 
302  | 	Sprintf(qbuf,
303  | 		"\"Wilt thou settle for a minor consultation?\" (%d zorkmids)",
304  | 		minor_cost);
305  | 	switch (ynq(qbuf)) {
306  | 	    default:
307  | 	    case 'q':
308  | 		return 0;
309  | 	    case 'y':
310  | 		if (u.ugold < (long)minor_cost) {
311  | 		    You("don't even have enough money for that!");
312  | 		    return 0;
313  | 		}
314  | 		u_pay = minor_cost;
315  | 		break;
316  | 	    case 'n':
317  | 		if (u.ugold <= (long)minor_cost ||	/* don't even ask */
318  | 		    (oracle_cnt == 1 || oracle_flg < 0)) return 0;
319  | 		Sprintf(qbuf,
320  | 			"\"Then dost thou desire a major one?\" (%d zorkmids)",
321  | 			major_cost);
322  | 		if (yn(qbuf) != 'y') return 0;
323  | 		u_pay = (u.ugold < (long)major_cost ? (int)u.ugold
324  | 						    : major_cost);
325  | 		break;
326  | 	}
327  | 	u.ugold -= (long)u_pay;
328  | 	oracl->mgold += (long)u_pay;
329  | 	flags.botl = 1;
330  | 	add_xpts = 0;	/* first oracle of each type gives experience points */
331  | 	if (u_pay == minor_cost) {
332  | 		outrumor(1, BY_ORACLE);
333  | 		if (!u.uevent.minor_oracle)
334  | 		    add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
335  | 		    /* 5 pts if very 1st, or 2 pts if major already done */
336  | 		u.uevent.minor_oracle = TRUE;
337  | 	} else {
338  | 		boolean cheapskate = u_pay < major_cost;
339  | 		outoracle(cheapskate, TRUE);
340  | 		if (!cheapskate && !u.uevent.major_oracle)
341  | 		    add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
342  | 		    /* ~100 pts if very 1st, ~40 pts if minor already done */
343  | 		u.uevent.major_oracle = TRUE;
344  | 		exercise(A_WIS, !cheapskate);
345  | 	}
346  | 	if (add_xpts) {
347  | 		more_experienced(add_xpts, u_pay/50);
348  | 		newexplevel();
349  | 	}
350  | 	return 1;
351  | }
352  | 
353  | /*rumors.c*/