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

s16b borg_active = 0;		/* Activation level */

bool borg_prompt = FALSE;	/* Prompt/Message available */

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_level = FALSE;	/* Save savefile when level changes */

bool borg_flag_save_depth = FALSE;	/* Save savefile when depth changes */


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

s16b borg_boost;		/* Current boosted 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 */

bool borg_need_save;	/* Borg needs to save the game */


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

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

sint amt_cure_critical;
sint amt_cure_serious;

sint amt_detect_trap;
sint amt_detect_door;

sint amt_missile;

sint amt_book[9];

sint amt_add_stat[6];
sint amt_fix_stat[6];
sint amt_fix_exp;

sint amt_enchant_to_a;
sint amt_enchant_to_d;
sint amt_enchant_to_h;


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

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

sint num_cure_critical;
sint num_cure_serious;

sint num_missile;

sint num_book[9];

sint num_fix_stat[6];

sint num_fix_exp;

sint num_enchant_to_a;
sint num_enchant_to_d;
sint 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 */



/*
 * 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 -- Array[?] to count racial appearances per level
 */

s16b *borg_race_count;


/*
 * Hack -- Array[?] to count racial kills (for uniques)
 */

s16b *borg_race_death;


/*
 * Array[256] for classification of map symbols (feature).
 */

byte *borg_char_feat;

/*
 * Arrays[256] for classification of map symbols (object/monster).
 */

bool *borg_char_is_take;

bool *borg_char_is_kill;


/*
 * Array[BORG_VIEW_MAX] of grids used by "update_view()".
 */

sint borg_view_n = 0;

u16b *borg_view_g;


/*
 * Arrays[BORG_TEMP_MAX] of grids used for various things.
 *
 * Note that "borg_temp_g" and "borg_temp_y"/"borg_temp_x" share memory.
 */

sint borg_temp_n = 0;

u16b *borg_temp_g;

byte *borg_temp_y;
byte *borg_temp_x;


/*
 * Arrays[BORG_HACK_MAX] of grids used for tracking lit floor grids.
 *
 * Note that "borg_hack_g" and "borg_hack_y"/"borg_hack_x" share memory.
 */

sint borg_hack_n = 0;

u16b *borg_hack_g;

byte *borg_hack_y;
byte *borg_hack_x;


/*
 * Arrays[BORG_FLOW_MAX] of grids used for spreading flow information.
 *
 * Note that "borg_flow_g" and "borg_flow_y"/"borg_flow_x" share memory.
 */

sint borg_flow_head = 0;
sint borg_flow_tail = 0;

u16b *borg_flow_g;

byte *borg_flow_y;
byte *borg_flow_x;


/*
 * Arrays[DUNGEON_HGT][256] of dungeon info flags / feature codes.
 */

byte (*borg_cave_info)[256];
byte (*borg_cave_feat)[256];


/*
 * Arrays[DUNGEON_HGT][DUNGEON_WID] of dungeon objects / monsters.
 */

byte (*borg_cave_o_idx)[DUNGEON_WID];
byte (*borg_cave_m_idx)[DUNGEON_WID];


/*
 * Array[DUNGEON_HGT][DUNGEON_WID] of search counts.
 */

byte (*borg_cave_search)[DUNGEON_WID];


/*
 * Array[DUNGEON_HGT][DUNGEON_WID] of cached danger values.
 */

s16b (*borg_cave_danger)[DUNGEON_WID];


/*
 * Arrays[DUNGEON_HGT][DUNGEON_WID] of active/working flow costs.
 */

byte (*borg_flow_cost)[DUNGEON_WID];
byte (*borg_flow_work)[DUNGEON_WID];




/*
 * Strategy flags -- recalculate things
 */

bool borg_do_wipe_danger = FALSE;	/* Recalculate danger */

bool borg_do_update_view = FALSE;	/* Recalculate view */


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



/*
 * Hack -- allow fast access to any term windows.  XXX XXX XXX
 */
const term *borg_term_pointer = NULL;


/*
 * Query the "attr/char" at a given location on the screen
 *
 * Note that "x,y" specifies a screen location, which is assumed to be
 * legal.  XXX XXX XXX
 *
 * We always return 0.
 */
errr borg_what_char(int x, int y, byte *a, char *c)
{
	const byte *aa;
	const char *cc;

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

	/* Access */
	(*a) = (*aa);
	(*c) = (*cc);

	/* Success */
	return (0);
}


/*
 * Query the "attr/char codes" at a given location on the screen
 *
 * This function extracts the attr/char codes of some (partial) row of the
 * current screen, called a "region", starting at some given location, and
 * having a maximal length which is specified by the user, and an actual
 * length which is limited to the longest sequence of attr/char codes which
 * share the same "attr" code.
 *
 * Note that "x,y" specifies the starting screen location, which is assumed
 * to be legal.  XXX XXX XXX
 *
 * Note that "n" is used to determine the desired length "m" of the region,
 * equal to "MIN((80-x),ABS(n))", though the actual length of the region is
 * limited to the longest sequence of attr/char codes which share the same
 * "attr" code.
 *
 * Note that if "n" is non-negative, and the actual length of the region is
 * not equal to "n", then we will return 1, otherwise, we will return 0.
 *
 * Note that if "n" is negative, we will always return 0.
 *
 * Note that "a" points to a single "attr", into which the attr code of the
 * region will be stored.
 *
 * Note that "s" points to an array of char's, of "sufficient" size, into
 * which the char codes of the region will be stored.  Note that we always
 * terminate this array of chars, so there must be room for a termination
 * character (to be safe, provide at least 81 characters).  XXX XXX XXX
 *
 * We automatically convert all "blanks" and "invisible text" into spaces,
 * and we ignore the attr of such char's, except that if the entire region
 * consists of such char's, the attr of the region will be zero.
 *
 * We do not strip final spaces, so this function will very often read all
 * the way to the end of the line.
 *
 * This function assumes that "borg_term_pointer" points to a valid "term",
 * which is at least size "80x24".  Otherwise, core dumps are possible.
 *
 * The results of this function may be unexpected if any relevant grids
 * contain "graphic" attr/char pairs.  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;

	const byte *aa;
	const char *cc;

	/* Current attribute */
	byte d_a = 0;

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

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

	/* Direct access XXX XXX XXX */
	aa = &(borg_term_pointer->scr->a[y][x]);
	cc = &(borg_term_pointer->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;

	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);


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


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

	/* Send the keypresses */
	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)
{
	/* Hack -- Hard cancel */
	borg_active = 0;

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

