1/*	SCCS Id: @(#)rumors.c	3.4	1996/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
31STATIC_DCL void FDECL(init_rumors, (dlb *));
32STATIC_DCL void FDECL(init_oracles, (dlb *));
33
34static long true_rumor_start,  true_rumor_size,  true_rumor_end,
35	    false_rumor_start, false_rumor_size, false_rumor_end;
36static int oracle_flg = 0;  /* -1=>don't use, 0=>need init, 1=>init done */
37static unsigned oracle_cnt = 0;
38static long *oracle_loc = 0;
39
40STATIC_OVL void
41init_rumors(fp)
42dlb *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 */
66char *
67getrumor(truth, rumor_buf, exclude_cookie)
68int truth; /* 1=true, -1=false, 0=either */
69char *rumor_buf;
70boolean 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
137void
138outrumor(truth, mechanism)
139int truth; /* 1=true, -1=false, 0=either */
140int 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) {
150	    /* deal with various things that prevent reading */
151	    if (is_fainted() && mechanism == BY_COOKIE)
152	    	return;
153	    else if (Blind) {
154		if (mechanism == BY_COOKIE)
155			pline(fortune_msg);
156		pline("What a pity that you cannot read it!");
157	    	return;
158	    }
159	}
160	line = getrumor(truth, buf, reading ? FALSE : TRUE);
161	if (!*line)
162		line = "NetHack rumors file closed for renovation.";
163	switch (mechanism) {
164	    case BY_ORACLE:
165	 	/* Oracle delivers the rumor */
166		pline("True to her word, the Oracle %ssays: ",
167		  (!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " :
168		  (rn2(2) ? "nonchalantly " : ""))));
169		verbalize("%s", line);
170		exercise(A_WIS, TRUE);
171		return;
172	    case BY_COOKIE:
173		pline(fortune_msg);
174		/* FALLTHRU */
175	    case BY_PAPER:
176		pline("It reads:");
177		break;
178	}
179	pline("%s", line);
180}
181
182STATIC_OVL void
183init_oracles(fp)
184dlb *fp;
185{
186	register int i;
187	char line[BUFSZ];
188	int cnt = 0;
189
190	/* this assumes we're only called once */
191	(void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/
192	(void) dlb_fgets(line, sizeof line, fp);
193	if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) {
194	    oracle_cnt = (unsigned) cnt;
195	    oracle_loc = (long *) alloc((unsigned)cnt * sizeof (long));
196	    for (i = 0; i < cnt; i++) {
197		(void) dlb_fgets(line, sizeof line, fp);
198		(void) sscanf(line, "%5lx\n", &oracle_loc[i]);
199	    }
200	}
201	return;
202}
203
204void
205save_oracles(fd, mode)
206int fd, mode;
207{
208	if (perform_bwrite(mode)) {
209	    bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
210	    if (oracle_cnt)
211		bwrite(fd, (genericptr_t)oracle_loc, oracle_cnt*sizeof (long));
212	}
213	if (release_data(mode)) {
214	    if (oracle_cnt) {
215		free((genericptr_t)oracle_loc);
216		oracle_loc = 0,  oracle_cnt = 0,  oracle_flg = 0;
217	    }
218	}
219}
220
221void
222restore_oracles(fd)
223int fd;
224{
225	mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
226	if (oracle_cnt) {
227	    oracle_loc = (long *) alloc(oracle_cnt * sizeof (long));
228	    mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof (long));
229	    oracle_flg = 1;	/* no need to call init_oracles() */
230	}
231}
232
233void
234outoracle(special, delphi)
235boolean special;
236boolean delphi;
237{
238	char	line[COLNO];
239	char	*endp;
240	dlb	*oracles;
241	int oracle_idx;
242	char xbuf[BUFSZ];
243
244	if(oracle_flg < 0 ||			/* couldn't open ORACLEFILE */
245	   (oracle_flg > 0 && oracle_cnt == 0))	/* oracles already exhausted */
246		return;
247
248	oracles = dlb_fopen(ORACLEFILE, "r");
249
250	if (oracles) {
251		winid tmpwin;
252		if (oracle_flg == 0) {	/* if this is the first outoracle() */
253			init_oracles(oracles);
254			oracle_flg = 1;
255			if (oracle_cnt == 0) return;
256		}
257		/* oracle_loc[0] is the special oracle;		*/
258		/* oracle_loc[1..oracle_cnt-1] are normal ones	*/
259		if (oracle_cnt <= 1 && !special) return;  /*(shouldn't happen)*/
260		oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
261		(void) dlb_fseek(oracles, oracle_loc[oracle_idx], SEEK_SET);
262		if (!special) oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];
263
264		tmpwin = create_nhwindow(NHW_TEXT);
265		if (delphi)
266		    putstr(tmpwin, 0, special ?
267		          "The Oracle scornfully takes all your money and says:" :
268		          "The Oracle meditates for a moment and then intones:");
269		else
270		    putstr(tmpwin, 0, "The message reads:");
271		putstr(tmpwin, 0, "");
272
273		while(dlb_fgets(line, COLNO, oracles) && strcmp(line,"---\n")) {
274			if ((endp = index(line, '\n')) != 0) *endp = 0;
275			putstr(tmpwin, 0, xcrypt(line, xbuf));
276		}
277		display_nhwindow(tmpwin, TRUE);
278		destroy_nhwindow(tmpwin);
279		(void) dlb_fclose(oracles);
280	} else {
281		pline("Can't open oracles file!");
282		oracle_flg = -1;	/* don't try to open it again */
283	}
284}
285
286int
287doconsult(oracl)
288register struct monst *oracl;
289{
290#ifdef GOLDOBJ
291        long umoney = money_cnt(invent);
292#endif
293	int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
294	int add_xpts;
295	char qbuf[QBUFSZ];
296
297	multi = 0;
298
299	if (!oracl) {
300		There("is no one here to consult.");
301		return 0;
302	} else if (!oracl->mpeaceful) {
303		pline("%s is in no mood for consultations.", Monnam(oracl));
304		return 0;
305#ifndef GOLDOBJ
306	} else if (!u.ugold) {
307#else
308	} else if (!umoney) {
309#endif
310		You("have no money.");
311		return 0;
312	}
313
314	Sprintf(qbuf,
315		"\"Wilt thou settle for a minor consultation?\" (%d %s)",
316		minor_cost, currency((long)minor_cost));
317	switch (ynq(qbuf)) {
318	    default:
319	    case 'q':
320		return 0;
321	    case 'y':
322#ifndef GOLDOBJ
323		if (u.ugold < (long)minor_cost) {
324#else
325		if (umoney < (long)minor_cost) {
326#endif
327		    You("don't even have enough money for that!");
328		    return 0;
329		}
330		u_pay = minor_cost;
331		break;
332	    case 'n':
333#ifndef GOLDOBJ
334		if (u.ugold <= (long)minor_cost ||	/* don't even ask */
335#else
336		if (umoney <= (long)minor_cost ||	/* don't even ask */
337#endif
338		    (oracle_cnt == 1 || oracle_flg < 0)) return 0;
339		Sprintf(qbuf,
340			"\"Then dost thou desire a major one?\" (%d %s)",
341			major_cost, currency((long)major_cost));
342		if (yn(qbuf) != 'y') return 0;
343#ifndef GOLDOBJ
344		u_pay = (u.ugold < (long)major_cost ? (int)u.ugold
345						    : major_cost);
346#else
347		u_pay = (umoney < (long)major_cost ? (int)umoney
348						    : major_cost);
349#endif
350		break;
351	}
352#ifndef GOLDOBJ
353	u.ugold -= (long)u_pay;
354	oracl->mgold += (long)u_pay;
355#else
356        money2mon(oracl, (long)u_pay);
357#endif
358	flags.botl = 1;
359	add_xpts = 0;	/* first oracle of each type gives experience points */
360	if (u_pay == minor_cost) {
361		outrumor(1, BY_ORACLE);
362		if (!u.uevent.minor_oracle)
363		    add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
364		    /* 5 pts if very 1st, or 2 pts if major already done */
365		u.uevent.minor_oracle = TRUE;
366	} else {
367		boolean cheapskate = u_pay < major_cost;
368		outoracle(cheapskate, TRUE);
369		if (!cheapskate && !u.uevent.major_oracle)
370		    add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
371		    /* ~100 pts if very 1st, ~40 pts if minor already done */
372		u.uevent.major_oracle = TRUE;
373		exercise(A_WIS, !cheapskate);
374	}
375	if (add_xpts) {
376		more_experienced(add_xpts, u_pay/50);
377		newexplevel();
378	}
379	return 1;
380}
381
382/*rumors.c*/
383