/*	SCCS Id: @(#)rumors.c	3.1	92/12/05	*/
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"

/*	[note: this comment is fairly old, but still accurate for 3.1]
 * Rumors have been entirely rewritten to speed up the access.  This is
 * essential when working from floppies.  Using fseek() the way that's done
 * here means rumors following longer rumors are output more often than those
 * following shorter rumors.  Also, you may see the same rumor more than once
 * in a particular game (although the odds are highly against it), but
 * this also happens with real fortune cookies.  -dgk
 */

/*	3.1
 * The rumors file consists of a "do not edit" line, a hexadecimal number
 * giving the number of bytes of useful/true rumors, followed by those
 * true rumors (one per line), followed by the useless/false/misleading/cute
 * rumors (also one per line).  Number of bytes of untrue rumors is derived
 * via fseek(EOF)+ftell().
 *
 * The oracles file consists of a "do not edit" comment, a decimal count N
 * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
 * records, separated by "---" lines.  The first oracle is a special case,
 * and placed there by 'makedefs'.
 */

#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifndef SEEK_CUR
# define SEEK_CUR 1
#endif
#ifndef SEEK_END	/* aka SEEK_EOF */
# define SEEK_END 2
#endif

static void FDECL(init_rumors, (FILE *));
static void FDECL(init_oracles, (FILE *));
static void FDECL(outoracle, (BOOLEAN_P));

static long true_rumor_start,  true_rumor_size,  true_rumor_end,
	    false_rumor_start, false_rumor_size, false_rumor_end;
static int oracle_flg = 0;  /* -1=>don't use, 0=>need init, 1=>init done */
static unsigned oracle_cnt = 0;
static long *oracle_loc = 0;

static void
init_rumors(fp)
FILE *fp;
{
	char line[BUFSZ];

	(void) fgets(line, sizeof line, fp);	/* skip "don't edit" comment */
	if (fscanf(fp, "%6lx\n", &true_rumor_size) == 1 &&
	    true_rumor_size > 0L) {
	    (void) fseek(fp, 0L, SEEK_CUR);
	    true_rumor_start  = ftell(fp);
	    true_rumor_end    = true_rumor_start + true_rumor_size;
	    (void) fseek(fp, 0L, SEEK_END);
	    false_rumor_end   = ftell(fp);
	    false_rumor_start = true_rumor_end;	/* ok, so it's redundant... */
	    false_rumor_size  = false_rumor_end - false_rumor_start;
	} else
	    true_rumor_size = -1L;	/* init failed */
}

char *
getrumor(truth)
int truth; /* 1=true, -1=false, 0=either */
{
	static char rumor_buf[COLNO + 28];
	FILE	*rumors;
	long tidbit, beginning;
	char	*endp, line[sizeof rumor_buf];

	rumor_buf[0] = '\0';
	if (true_rumor_size < 0L)	/* we couldn't open RUMORFILE */
		return rumor_buf;

	rumors = fopen_datafile(RUMORFILE, "r");

	if (rumors) {
		if (true_rumor_size == 0L) {	/* if this is 1st outrumor() */
		    init_rumors(rumors);
		    if (true_rumor_size < 0L) {	/* init failed */
			Sprintf(rumor_buf, "Error reading \"%.80s\".",
				RUMORFILE);
			return rumor_buf;
		    }
		}
		/*
		 *	input:      1    0   -1
		 *	 rn2 \ +1  2=T  1=T  0=F
		 *	 adj./ +0  1=T  0=F -1=F
		 */
		switch (truth += rn2(2)) {
		  case  2:	/*(might let a bogus input arg sneak thru)*/
		  case  1:  beginning = true_rumor_start;
			    tidbit = rand() % true_rumor_size;
			break;
		  case  0:	/* once here, 0 => false rather than "either"*/
		  case -1:  beginning = false_rumor_start;
			    tidbit = rand() % false_rumor_size;
			break;
		  default:
			    impossible("strange truth value for rumor");
			return strcpy(rumor_buf, "Oops...");
		}
		(void) fseek(rumors, beginning + tidbit, SEEK_SET);
		(void) fgets(line, sizeof line, rumors);
		if (!fgets(line, sizeof line, rumors) ||
		    (truth > 0 && ftell(rumors) > true_rumor_end)) {
			/* reached end of rumors -- go back to beginning */
			(void) fseek(rumors, beginning, SEEK_SET);
			(void) fgets(line, sizeof line, rumors);
		}
		if ((endp = index(line, '\n')) != 0) *endp = 0;
		Strcat(rumor_buf, xcrypt(line));
		(void) fclose(rumors);
		exercise(A_WIS, (truth > 0));
	} else {
		pline("Can't open rumors file!");
		true_rumor_size = -1;	/* don't try to open it again */
	}
	return rumor_buf;
}

void
outrumor(truth, cookie)
int truth; /* 1=true, -1=false, 0=either */
boolean cookie;
{
	static const char fortune_msg[] =
		"This cookie has a scrap of paper inside.";
	const char *line;

	if (cookie && Blind) {
		pline(fortune_msg);
		pline("What a pity that you cannot read it!");
		return;
	}
	line = getrumor(truth);
	if (!*line)
		line = "NetHack rumors file closed for renovation.";
	if (cookie) {
		pline(fortune_msg);
		pline("It reads:");
		pline("%s", line);
	} else {	/* if the Oracle is the only alternative */
		pline("True to her word, the Oracle %ssays: ",
		(!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " :
		(rn2(2) ? "nonchalantly " : ""))));
		verbalize("%s", line);
		exercise(A_WIS, TRUE);
	}
}

static void
init_oracles(fp)
FILE *fp;
{
	register int i;
	char line[BUFSZ];
	int cnt = 0;

	/* this assumes we're only called once */
	(void) fgets(line, sizeof line, fp);	/* skip "don't edit" comment */
	if (fscanf(fp, "%5d", &cnt) == 1 && cnt > 0) {
	    oracle_cnt = (unsigned) cnt;
	    oracle_loc = (long *) alloc(cnt * sizeof (long));
	    for (i = 0; i < cnt; i++)
		(void) fscanf(fp, "%5lx", &oracle_loc[i]);
	}
	return;
}

void
save_oracles(fd)
int fd;
{
	bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
	if (oracle_cnt)
	    bwrite(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof (long));
}

void
restore_oracles(fd)
int fd;
{
	mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
	if (oracle_cnt) {
	    oracle_loc = (long *) alloc(oracle_cnt * sizeof (long));
	    mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof (long));
	    oracle_flg = 1;	/* no need to call init_oracles() */
	}
}

static void
outoracle(special)
boolean special;
{
	char	line[COLNO];
	char	*endp;
	FILE	*oracles;
	int oracle_idx;

	if(oracle_flg < 0 ||			/* couldn't open ORACLEFILE */
	   (oracle_flg > 0 && oracle_cnt == 0))	/* oracles already exhausted */
		return;

	oracles = fopen_datafile(ORACLEFILE, "r");

	if (oracles) {
		winid tmpwin;
		if (oracle_flg == 0) {	/* if this is the first outoracle() */
			init_oracles(oracles);
			oracle_flg = 1;
			if (oracle_cnt == 0) return;
		}
		/* oracle_loc[0] is the special oracle;		*/
		/* oracle_loc[1..oracle_cnt-1] are normal ones	*/
		if (oracle_cnt <= 1 && !special) return;  /*(shouldn't happen)*/
		oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
		(void) fseek(oracles, oracle_loc[oracle_idx], SEEK_SET);
		if (!special) oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];

		tmpwin = create_nhwindow(NHW_TEXT);
		putstr(tmpwin, 0, special ?
		      "The Oracle scornfully takes all your money and says:" :
		      "The Oracle meditates for a moment and then intones:");
		putstr(tmpwin, 0, "");

		while (fgets(line, COLNO, oracles) && strcmp(line,"---\n")) {
			if ((endp = index(line, '\n')) != 0) *endp = 0;
			putstr(tmpwin, 0, xcrypt(line));
		}
		display_nhwindow(tmpwin, TRUE);
		destroy_nhwindow(tmpwin);
		(void) fclose(oracles);
	} else {
		pline("Can't open oracles file!");
		oracle_flg = -1;	/* don't try to open it again */
	}
}

int
doconsult(oracl)
register struct monst *oracl;
{
	int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
	int add_xpts;
	char qbuf[QBUFSZ];

	multi = 0;

	if (!oracl) {
		pline("There is no one here to consult.");
		return 0;
	} else if (!oracl->mpeaceful) {
		pline("The Oracle is in no mood for consultations.");
		return 0;
	} else if (!u.ugold) {
		You("have no money.");
		return 0;
	}

	Sprintf(qbuf,
		"\"Wilt thou settle for a minor consultation?\" (%d zorkmids)",
		minor_cost);
	switch (ynq(qbuf)) {
	    default:
	    case 'q':
		return 0;
	    case 'y':
		if (u.ugold < (long)minor_cost) {
		    You("don't even have enough money for that!");
		    return 0;
		}
		u_pay = minor_cost;
		break;
	    case 'n':
		if (u.ugold <= (long)minor_cost ||	/* don't even ask */
		    (oracle_cnt == 1 || oracle_flg < 0)) return 0;
		Sprintf(qbuf,
			"\"Then dost thou desire a major one?\" (%d zorkmids)",
			major_cost);
		if (yn(qbuf) != 'y') return 0;
		u_pay = (u.ugold < (long)major_cost ? (int)u.ugold
						    : major_cost);
		break;
	}
	u.ugold -= (long)u_pay;
	oracl->mgold += (long)u_pay;
	flags.botl = 1;
	add_xpts = 0;	/* first oracle of each type gives experience points */
	if (u_pay == minor_cost) {
		outrumor(1, FALSE);
		if (!u.uevent.minor_oracle)
		    add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
		    /* 5 pts if very 1st, or 2 pts if major already done */
		u.uevent.minor_oracle = TRUE;
	} else {
		boolean cheapskate = u_pay < major_cost;
		outoracle(cheapskate);
		if (!cheapskate && !u.uevent.major_oracle)
		    add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
		    /* ~100 pts if very 1st, ~40 pts if minor already done */
		u.uevent.major_oracle = TRUE;
		exercise(A_WIS, !cheapskate);
	}
	if (add_xpts) {
		more_experienced(add_xpts, u_pay/50);
		newexplevel();
	}
	return 1;
}

/*rumors.c*/
