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*/