/* File: borg1.c */

/*
 * Copyright (c) 1997 Ben Harrison
 *
 * This software may be copied and distributed for educational, research,
 * and not for profit purposes provided that this copyright and statement
 * are included in all such copies.
 */

#include "angband.h"

#include "borg.h"


#ifdef ALLOW_BORG


/*
 * This file contains various low level variables and routines.
 */




/*
 * Control variables
 */

bool borg_active;		/* Actually active */

bool borg_cancel;		/* Being cancelled */

u16b borg_step = 0;		/* Step count (if any) */

char *borg_key_queue;	/* Keypress queue */
s16b borg_key_head;		/* Head of queue */
s16b borg_key_tail;		/* Tail of queue */

FILE *borg_fff = NULL;	/* Log file */

char borg_match_string[128] = "";	/* Search string */


/*
 * Optional strategy flags
 */

bool borg_flag_save = FALSE;	/* Save savefile at each level */

bool borg_flag_dump = FALSE;	/* Save savefile at each death */



/* 
 * Optional cheating flags
 */

bool borg_flag_cheat_equip;		/* Cheat for "equip mode" */

bool borg_flag_cheat_inven;		/* Cheat for "inven mode" */

bool borg_flag_cheat_spell;		/* Cheat for "browse mode" */

bool borg_flag_cheat_panel;		/* Cheat for "panel mode" */


/*
 * Internal random number generator
 */

bool borg_rand_quick;		/* Save system setting */

u32b borg_rand_value;		/* Save system setting */

u32b borg_rand_local;		/* Save personal setting */


/*
 * Time variables
 */

s16b borg_time = 0L;			/* Current "time" */

s16b borg_when_began;			/* When this level began */

s16b borg_when_call_lite;		/* When we last did call light */
s16b borg_when_wizard_lite;		/* When we last did wizard light */

s16b borg_when_detect_traps;	/* When we last detected traps */
s16b borg_when_detect_doors;	/* When we last detected doors */
s16b borg_when_detect_walls;	/* When we last detected walls */


/*
 * Some information
 */

s16b borg_task;			/* Current goal type */

s16b borg_avoid;		/* Current danger avoidance */

bool borg_rising;		/* Currently returning to town */

bool borg_leaving;		/* Currently leaving the level */

bool borg_fleeing;		/* Currently fleeing the level */

bool borg_ignoring;		/* Currently ignoring monsters */

bool borg_recalling;	/* Currently waiting for recall */

bool borg_completed;	/* Currently bored with dungeon */

bool borg_stair_less;	/* Use the next "up" staircase */
bool borg_stair_more;	/* Use the next "down" staircase */

s16b borg_goal_shop = -1;		/* Next shop to visit */
s16b borg_goal_ware = -1;		/* Next item to buy there */
s16b borg_goal_item = -1;		/* Next item to sell there */

s16b borg_exam_item = -1;		/* Hack -- examine item -- index */

char borg_exam_note[32];		/* Hack -- examine item -- note */


/*
 * Various "amounts" (for the player)
 */

s16b amt_fuel;
s16b amt_food;
s16b amt_ident;
s16b amt_recall;
s16b amt_phase;
s16b amt_escape;
s16b amt_teleport;

s16b amt_cure_critical;
s16b amt_cure_serious;

s16b amt_detect_trap;
s16b amt_detect_door;

s16b amt_missile;

s16b amt_book[9];

s16b amt_add_stat[6];
s16b amt_fix_stat[6];
s16b amt_fix_exp;

s16b amt_enchant_to_a;
s16b amt_enchant_to_d;
s16b amt_enchant_to_h;


/*
 * Various "amounts" (for the home)
 */

s16b num_fuel;
s16b num_food;
s16b num_ident;
s16b num_recall;
s16b num_phase;
s16b num_escape;
s16b num_teleport;

s16b num_cure_critical;
s16b num_cure_serious;

s16b num_missile;

s16b num_book[9];

s16b num_fix_stat[6];

s16b num_fix_exp;

s16b num_enchant_to_a;
s16b num_enchant_to_d;
s16b num_enchant_to_h;


/*
 * State variables extracted from various places
 */

int borg_base_depth;		/* Actual dungeon "level" */

int borg_base_level;		/* Actual player level */

s32b borg_base_exp;			/* Actual experience */

s32b borg_base_au;			/* Actual gold */

int borg_base_pspeed;		/* Actual speed */

int borg_base_ac;			/* Actual ac */

int borg_base_chp;			/* Actual current hitpoints */
int borg_base_mhp;			/* Actual maximum hitpoints */

int borg_base_csp;			/* Actual current spell points */
int borg_base_msp;			/* Actual maximum spell points */

int borg_base_stat[6];		/* Actual stat values XXX XXX */

int borg_base_book[9];		/* Actual book slots XXX XXX */

int borg_base_shop = -1;	/* Actual shop index */

int borg_base_wgt;			/* Actual weight */

bool borg_base_is_weak;		/* Actual flag -- weak */
bool borg_base_is_hungry;	/* Actual flag -- hungry/weak */

bool borg_base_is_full;		/* Actual flag -- full/gorged */
bool borg_base_is_gorged;	/* Actual flag -- gorged */

bool borg_base_is_blind;	/* Actual flag -- blind */
bool borg_base_is_afraid;	/* Actual flag -- afraid */
bool borg_base_is_confused;	/* Actual flag -- confused */
bool borg_base_is_poisoned;	/* Actual flag -- poisoned */

bool borg_base_is_cut;		/* Actual flag -- bleeding */
bool borg_base_is_stun;		/* Actual flag -- stunned */

bool borg_base_is_image;	/* Actual flag -- hallucinating */
bool borg_base_is_study;	/* Actual flag -- may learn spells */

bool borg_base_fix_lev;		/* Actual flag -- Drained LEV */
bool borg_base_fix_exp;		/* Actual flag -- Drained EXP */

bool borg_base_fix_stat[6];	/* Actual flags -- Drained Stats XXX XXX */

int borg_feeling;		/* Recent level "feeling" */

int borg_happy_depth;	/* Favorite dungeon depth */
int borg_happy_count;	/* Number of completions */

s32b borg_base_power;	/* Hack -- base power */

bool borg_failure;		/* Notice failure */

bool borg_simulate;		/* Simulation flag */

bool borg_expected;		/* Expected danger */



/*
 * Some global structures
 */

static player_type borg_player_type_body;

player_type *b_ptr = &borg_player_type_body;

static player_xtra borg_player_xtra_body;

player_xtra *xb_ptr = &borg_player_xtra_body;

player_race *rb_ptr;	/* Player race info */
player_class *cb_ptr;	/* Player class info */

player_magic *mb_ptr;	/* Player magic info */

player_anal *ab_ptr;	/* Player anal info */

auto_dungeon *db_ptr;	/* Dungeon info */


/*
 * Hack -- message memory
 */

s16b borg_msg_len;
s16b borg_msg_siz;
char *borg_msg_buf;
s16b borg_msg_num;
s16b borg_msg_max;
s16b *borg_msg_pos;
s16b *borg_msg_use;



/*
 * Hack -- the detection arrays
 */

bool borg_detect_wall[6][6];

bool borg_detect_trap[6][6];

bool borg_detect_door[6][6];


/*
 * Track shops
 */

byte *borg_track_shop_x;
byte *borg_track_shop_y;


/*
 * Track stairs up
 */

s16b borg_track_less_num;
byte *borg_track_less_x;
byte *borg_track_less_y;


/*
 * Track stairs down
 */

s16b borg_track_more_num;
byte *borg_track_more_x;
byte *borg_track_more_y;


/*
 * Track interesting grids
 */

s16b borg_wank_num = 0;

auto_wank *borg_wanks;


/*
 * Track interesting objects
 */

s16b borg_takes_cnt;

s16b borg_takes_nxt;

auto_take borg_takes[BORG_MAX_TAKE];


/*
 * Track interesting monsters
 */

s16b borg_kills_cnt;

s16b borg_kills_nxt;

auto_kill borg_kills[BORG_MAX_KILL];


/*
 * Hack -- extra fear per "region"
 */

u16b borg_fear_region[6][18];


/*
 * Hack -- count racial appearances per level
 */

s16b *borg_race_count;


/*
 * Hack -- count racial kills (for uniques)
 */

s16b *borg_race_death;


/*
 * Classification of map symbols
 */

bool borg_char_is_take[256];		/* Symbol may be an object */

bool borg_char_is_kill[256];		/* Symbol may be a monster */


/*
 * Maintain a set of grids marked as "BORG_LITE"
 */

s16b borg_lite_n = 0;

byte borg_lite_x[AUTO_LITE_MAX];
byte borg_lite_y[AUTO_LITE_MAX];


/*
 * Maintain a set of grids marked as "BORG_VIEW"
 */

s16b borg_view_n = 0;

byte borg_view_x[AUTO_VIEW_MAX];
byte borg_view_y[AUTO_VIEW_MAX];


/*
 * Maintain a temporary set of grids
 */

s16b borg_temp_n = 0;

byte borg_temp_x[AUTO_TEMP_MAX];
byte borg_temp_y[AUTO_TEMP_MAX];



/*
 * Strategy flags -- recalculate things
 */

bool borg_do_wipe_danger = FALSE;	/* Recalculate danger */

bool borg_do_update_view = FALSE;	/* Recalculate view */

bool borg_do_update_lite = FALSE;	/* Recalculate lite */


/*
 * Strategy flags -- examine the world
 */

bool borg_do_inven = TRUE;	/* Acquire "inven" info */

bool borg_do_equip = TRUE;	/* Acquire "equip" info */

bool borg_do_panel = TRUE;	/* Acquire "panel" info */

bool borg_do_frame = TRUE;	/* Acquire "frame" info */

bool borg_do_spell = TRUE;	/* Acquire "spell" info */

byte borg_do_spell_aux = 0;	/* Hack -- book for "borg_do_spell" */

bool borg_do_browse = 0;	/* Acquire "store" info */

byte borg_do_browse_what = 0;	/* Hack -- store for "borg_do_browse" */

byte borg_do_browse_more = 0;	/* Hack -- pages for "borg_do_browse" */


/*
 * Strategy flags -- run certain functions
 */

bool borg_do_crush_junk = FALSE;

bool borg_do_crush_hole = FALSE;

bool borg_do_crush_slow = FALSE;


/*
 * Inventory and shops
 */

auto_item *borg_items;		/* Current "inventory" */

auto_shop *borg_shops;		/* Current "shops" */



/*
 * Safety arrays for simulating possible worlds
 */

auto_item *borg_safe_items;		/* Safety "inventory" */

auto_shop *borg_safe_shops;		/* Safety "shops" */


/*
 * Spell/Prayer info
 */

auto_magic borg_magics[9][9];	/* Magic info, by book/what */




/*
 * Query the "attr/char" at a given location on the screen
 * We return "zero" if the given location was legal
 *
 * We assume the given location is legal XXX XXX XXX
 */
errr borg_what_char(int x, int y, byte *a, char *c)
{
	/* Direct access XXX XXX XXX */
	(*a) = (Term->scr->a[y][x]);
	(*c) = (Term->scr->c[y][x]);

	/* Success */
	return (0);
}


/*
 * Query the "attr/chars" at a given location on the screen
 *
 * Note that "a" points to a single "attr", and "s" to an array
 * of "chars", into which the attribute and text at the given
 * location are stored.
 *
 * We will not grab more than "ABS(n)" characters for the string.
 * If "n" is "positive", we will grab exactly "n" chars, or fail.
 * If "n" is "negative", we will grab until the attribute changes.
 *
 * We automatically convert all "blanks" and "invisible text" into
 * spaces, and we ignore the attribute of such characters.
 *
 * We do not strip final spaces, so this function will very often
 * read characters all the way to the end of the line.
 *
 * We succeed only if a string of some form existed, and all of
 * the non-space characters in the string have the same attribute,
 * and the string was long enough.
 *
 * We assume the given location is legal XXX XXX XXX
 */
errr borg_what_text(int x, int y, int n, byte *a, char *s)
{
	int i;

	byte t_a;
	char t_c;

	byte *aa;
	char *cc;

	/* Current attribute */
	byte d_a = 0;

	/* Max length to scan for */
	int m = ABS(n);

	/* Hack -- Do not run off the screen */
	if (x + m > 80) m = 80 - x;

	/* Direct access XXX XXX XXX */
	aa = &(Term->scr->a[y][x]);
	cc = &(Term->scr->c[y][x]);

	/* Grab the string */
	for (i = 0; i < m; i++)
	{
		/* Access */
		t_a = *aa++;
		t_c = *cc++;

		/* Handle spaces */
		if ((t_c == ' ') || !t_a)
		{
			/* Save space */
			s[i] = ' ';
		}

		/* Handle real text */
		else
		{
			/* Attribute ready */
			if (d_a)
			{
				/* Verify the "attribute" (or stop) */
				if (t_a != d_a) break;
			}

			/* Acquire attribute */
			else
			{
				/* Save it */
				d_a = t_a;
			}

			/* Save char */
			s[i] = t_c;
		}
	}

	/* Terminate the string */
	s[i] = '\0';

	/* Save the attribute */
	(*a) = d_a;

	/* Too short */
	if ((n > 0) && (i != n)) return (1);

	/* Success */
	return (0);
}



/*
 * Steal info from the game -- inventory weight info
 */
void borg_steal_inventory_weight(char *buf)
{
	/* Create a fake description string */
	sprintf(buf, "Inventory (carrying %d.%d pounds)",
	        p_ptr->total_weight / 10, p_ptr->total_weight % 10);
}


/*
 * Steal info from the game -- inventory object description
 */
void borg_steal_inventory_desc(char *buf, int item)
{
	/* Default to "nothing" */
	buf[0] = '\0';

	/* Describe a real item */
	if (inventory[item].k_idx)
	{
		/* Describe it (fully) */
		object_desc(buf, &inventory[item], TRUE, 3);
	}
}



/*
 * Steal info from the game -- spell/prayer description
 *
 * Note the use of "as->cheat" to optimize the spell index extraction
 */
void borg_steal_spell_info(char *buf, int book, int what)
{
	int spell;

	int num = 0;

	magic_type *s_ptr;

	auto_magic *as;

	cptr comment;

	char info[80];


	/* Default (20 spaces) */
	strcpy(buf, "                    ");


	/* Access the spell */
	as = &borg_magics[book][what];

	/* Skip illegible spells */
	if (as->status == BORG_MAGIC_ICKY) return;


	/* Access the index */
	spell = as->cheat;

	/* Access the spell */
	s_ptr = &mp_ptr->info[spell];

	/* Illegible spells */
	if (s_ptr->slevel >= 99) return;


	/* Get extra info */
	spell_info(info, spell);

	/* Use that info */
	comment = info;

	/* Analyze the spell */
	if ((spell < 32) ?
	    ((p_ptr->spell_forgotten1 & (1L << spell))) :
	    ((p_ptr->spell_forgotten2 & (1L << (spell - 32)))))
	{
		comment = " forgotten";
	}
	else if (!((spell < 32) ?
	           (p_ptr->spell_learned1 & (1L << spell)) :
	           (p_ptr->spell_learned2 & (1L << (spell - 32)))))
	{
		comment = " unknown";
	}
	else if (!((spell < 32) ?
	           (p_ptr->spell_worked1 & (1L << spell)) :
	           (p_ptr->spell_worked2 & (1L << (spell - 32)))))
	{
		comment = " untried";
	}

	/* Extract the spell info */
	sprintf(buf, "%2d %4d %3d%%%s",
	        s_ptr->slevel, s_ptr->smana, spell_chance(spell), comment);
}


/*
 * Steal info from the game -- current dungeon panel info
 */
void borg_steal_panel_info(char *buf)
{
	/* Create a fake screen buffer */
	sprintf(buf, "Map sector [%d,%d]",
	        (p_ptr->wy / PANEL_HGT),
	        (p_ptr->wx / PANEL_WID));
}



/*
 * Log a message to a file
 */
void borg_info(cptr what)
{
	/* Dump a log file message */
	if (borg_fff) fprintf(borg_fff, "%s\n", what);
}



/*
 * Memorize a message, Log it, Search it, and Display it in pieces
 */
void borg_note(cptr what)
{
	int j, n, i, k;

	int w, h, x, y;

	term *old = Term;


	/* Memorize it */
	message_add(what);


	/* Log the message */
	borg_info(what);


	/* Mega-Hack -- Check against the search string */
	if (borg_match_string[0] && strstr(what, borg_match_string))
	{
		/* Clean cancel */
		borg_cancel = TRUE;
	}


	/* Scan windows */
	for (j = 0; j < 8; j++)
	{
		if (!angband_term[j]) continue;

		/* Check flag */
		if (!(op_ptr->window_flag[j] & PW_BORG_1)) continue;

		/* Activate */
		Term_activate(angband_term[j]);

		/* Access size */
		Term_get_size(&w, &h);

		/* Access cursor */
		Term_locate(&x, &y);

		/* Erase current line */
		Term_erase(0, y, 255);


		/* Total length */
		n = strlen(what);

		/* Too long */
		if (n > w - 2)
		{
			char buf[1024];

			/* Split */
			while (n > w - 2)
			{
				/* Default */
				k = w - 2;

				/* Find a split point */
				for (i = w / 2; i < w - 2; i++)
				{
					/* Pre-emptive split point */
					if (isspace(what[i])) k = i;
				}

				/* Copy over the split message */
				for (i = 0; i < k; i++)
				{
					/* Copy */
					buf[i] = what[i];
				}

				/* Indicate split */
				buf[i++] = '\\';

				/* Terminate */
				buf[i] = '\0';

				/* Show message */
				Term_addstr(-1, TERM_WHITE, buf);

				/* Advance (wrap) */
				if (++y >= h) y = 0;

				/* Erase next line */
				Term_erase(0, y, 255);

				/* Advance */
				what += k;

				/* Reduce */
				n -= k;
			}

			/* Show message tail */
			Term_addstr(-1, TERM_WHITE, what);

			/* Advance (wrap) */
			if (++y >= h) y = 0;

			/* Erase next line */
			Term_erase(0, y, 255);
		}

		/* Normal */
		else
		{
			/* Show message */
			Term_addstr(-1, TERM_WHITE, what);

			/* Advance (wrap) */
			if (++y >= h) y = 0;

			/* Erase next line */
			Term_erase(0, y, 255);
		}


		/* Flush output */
		Term_fresh();

		/* Use correct window */
		Term_activate(old);
	}
}



/*
 * Add a keypress to the "queue" (fake event)
 */
errr borg_keypress(char k)
{
	/* Hack -- Refuse to enqueue "nul" */
	if (!k) return (-1);

	/* Hack -- note the keypress */
	if (borg_fff) borg_info(format("& Key <%c>", k));

	/* Store the char, advance the queue */
	borg_key_queue[borg_key_head++] = k;

	/* Circular queue, handle wrap */
	if (borg_key_head == BORG_KEY_SIZE) borg_key_head = 0;

	/* Hack -- Catch overflow (forget oldest) */
	if (borg_key_head == borg_key_tail) borg_oops("overflow");

	/* Hack -- Overflow may induce circular queue */
	if (borg_key_tail == BORG_KEY_SIZE) borg_key_tail = 0;

	/* Success */
	return (0);
}


/*
 * Add a keypress to the "queue" (fake event)
 */
errr borg_keypresses(cptr str)
{
	cptr s;

	/* Enqueue them */
	for (s = str; *s; s++) borg_keypress(*s);

	/* Success */
	return (0);
}


/*
 * Get the next Borg keypress
 */
char borg_inkey(bool take)
{
	int i;

	/* Nothing ready */
	if (borg_key_head == borg_key_tail) return (0);

	/* Extract the keypress */
	i = borg_key_queue[borg_key_tail];

	/* Do not advance */
	if (!take) return (i);

	/* Advance the queue */
	borg_key_tail++;

	/* Circular queue requires wrap-around */
	if (borg_key_tail == BORG_KEY_SIZE) borg_key_tail = 0;

	/* Return the key */
	return (i);
}



/*
 * Get the next Borg keypress
 */
void borg_flush(void)
{
	/* Simply forget old keys */
	borg_key_tail = borg_key_head;
}




/*
 * Abort the Borg, noting the reason
 */
void borg_oops(cptr what)
{
	/* Stop processing */
	borg_active = FALSE;

	/* Give a warning */
	borg_note(format("# Aborting (%s).", what));

	/* Forget borg keys */
	borg_flush();
}





#else

#ifdef MACINTOSH
static int HACK = 0;
#endif /* MACINTOSH */

#endif /* ALLOW_BORG */
