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 
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
init_rumors(fp)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 *
getrumor(truth,rumor_buf,exclude_cookie)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_area(NH_RUMORAREA, 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 			    warning("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 		warning("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
outrumor(truth,mechanism)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) {
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 = "UnNetHack 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 
182 STATIC_OVL void
init_oracles(fp)183 init_oracles(fp)
184 dlb *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 
204 void
save_oracles(fd,mode)205 save_oracles(fd, mode)
206 int 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 
221 void
restore_oracles(fd)222 restore_oracles(fd)
223 int 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 
233 void
outoracle(special,delphi)234 outoracle(special, delphi)
235 boolean special;
236 boolean 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_area(NH_ORACLEAREA, 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 
286 #if 0 /* DEFERRED */
287 int
288 oracle_id(oracl)
289 register struct monst *oracl;
290 {
291 #ifdef GOLDOBJ
292     long umoney = money_cnt(invent);
293 #endif
294     char qbuf[QBUFSZ];
295     /* cost based on experience level similar to major consultations */
296     long identify_cost = 300 + 100 * u.ulevel;
297 
298     Sprintf(qbuf,
299 	"\"Perhaps thou would likest a divination?\" (%ld %s)",
300 	identify_cost, currency((long)identify_cost));
301 
302     if (yn(qbuf) == 'y') {
303 #ifndef GOLDOBJ
304 	if (u.ugold < (long)identify_cost) {
305 #else
306 	if (umoney < (long)identify_cost) {
307 #endif
308 	    pline("%s divines that you are short on cash.", Monnam(oracl));
309 	    return 0;
310 	}
311 #ifndef GOLDOBJ
312 	u.ugold -= (long)identify_cost;
313 	oracl->mgold += (long)identify_cost;
314 #else
315         money2mon(oracl, (long)identify_cost);
316 #endif
317 	flags.botl = 1;
318 	identify_pack(1);
319 	return 1;
320     }
321     return 0;
322 }
323 #endif
324 
325 int
doconsult(oracl)326 doconsult(oracl)
327 register struct monst *oracl;
328 {
329 #ifdef GOLDOBJ
330         long umoney = money_cnt(invent);
331 #endif
332 	int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
333 	int add_xpts;
334 	char qbuf[QBUFSZ];
335 
336 	multi = 0;
337 
338 	if (!oracl) {
339 		There("is no one here to consult.");
340 		return 0;
341 	} else if (!oracl->mpeaceful) {
342 		pline("%s is in no mood for consultations.", Monnam(oracl));
343 		return 0;
344 #ifndef GOLDOBJ
345 	} else if (!u.ugold) {
346 #else
347 	} else if (!umoney) {
348 #endif
349 		You("have no money.");
350 		return 0;
351 	}
352 
353 	Sprintf(qbuf,
354 		"\"Wilt thou settle for a minor consultation?\" (%d %s)",
355 		minor_cost, currency((long)minor_cost));
356 	switch (ynq(qbuf)) {
357 	    default:
358 	    case 'q':
359 		return 0;
360 	    case 'y':
361 #ifndef GOLDOBJ
362 		if (u.ugold < (long)minor_cost) {
363 #else
364 		if (umoney < (long)minor_cost) {
365 #endif
366 		    You("don't even have enough money for that!");
367 		    return 0;
368 		}
369 		u_pay = minor_cost;
370 		break;
371 	    case 'n':
372 #ifndef GOLDOBJ
373 		if (u.ugold <= (long)minor_cost ||	/* don't even ask */
374 #else
375 		if (umoney <= (long)minor_cost ||	/* don't even ask */
376 #endif
377 		    (oracle_cnt == 1 || oracle_flg < 0)) return 0;
378 		Sprintf(qbuf,
379 			"\"Then dost thou desire a major one?\" (%d %s)",
380 			major_cost, currency((long)major_cost));
381 		if (yn(qbuf) != 'y') return 0;
382 #ifndef GOLDOBJ
383 		u_pay = (u.ugold < (long)major_cost ? (int)u.ugold
384 						    : major_cost);
385 #else
386 		u_pay = (umoney < (long)major_cost ? (int)umoney
387 						    : major_cost);
388 #endif
389 		break;
390 	}
391 #ifndef GOLDOBJ
392 	u.ugold -= (long)u_pay;
393 	oracl->mgold += (long)u_pay;
394 #else
395         money2mon(oracl, (long)u_pay);
396 #endif
397 	flags.botl = 1;
398 	add_xpts = 0;	/* first oracle of each type gives experience points */
399 	if (u_pay == minor_cost) {
400 		outrumor(1, BY_ORACLE);
401 		if (!u.uevent.minor_oracle)
402 		    add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
403 		    /* 5 pts if very 1st, or 2 pts if major already done */
404 		u.uevent.minor_oracle = TRUE;
405 	} else {
406 		boolean cheapskate = u_pay < major_cost;
407 		outoracle(cheapskate, TRUE);
408 		if (!cheapskate && !u.uevent.major_oracle)
409 		    add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
410 		    /* ~100 pts if very 1st, ~40 pts if minor already done */
411 		u.uevent.major_oracle = TRUE;
412 		exercise(A_WIS, !cheapskate);
413 	}
414 	if (add_xpts) {
415 		more_experienced(add_xpts, add_xpts, u_pay/50);
416 		newexplevel();
417 	}
418 	return 1;
419 }
420 
421 /*rumors.c*/
422