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