/* File: bldg.c */

/*
 * Purpose: Building commands
 * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3
 * -KMW-
 *
 * Rewritten for Kangband 2.8.3i using Kamband's version of
 * bldg.c as written by Ivan Tkatchev
 *
 * Changed for ZAngband by Robert Ruehlmann
 *
 * Heavily modified for ToME by DarkGod
 */

#include "bldg.hpp"

#include "cave_type.hpp"
#include "cmd3.hpp"
#include "files.hpp"
#include "game.hpp"
#include "hooks.hpp"
#include "hook_quest_finish_in.hpp"
#include "hook_quest_fail_in.hpp"
#include "hook_init_quest_in.hpp"
#include "mimic.hpp"
#include "object1.hpp"
#include "object2.hpp"
#include "object_flag.hpp"
#include "owner_type.hpp"
#include "player_race_flag.hpp"
#include "player_type.hpp"
#include "q_library.hpp"
#include "q_fireprof.hpp"
#include "q_bounty.hpp"
#include "spells2.hpp"
#include "stats.hpp"
#include "store.hpp"
#include "store_action_type.hpp"
#include "store_info_type.hpp"
#include "store_type.hpp"
#include "tables.hpp"
#include "util.hpp"
#include "variable.hpp"
#include "xtra1.hpp"
#include "xtra2.hpp"
#include "z-form.hpp"
#include "z-rand.hpp"
#include "z-term.hpp"

/* remember building location */
static int building_loc = 0;


/*
 * A helper function for is_state
 */
static bool is_state_aux(store_type const *s_ptr, int state)
{
	auto const &ow_info = game->edit_data.ow_info;

	auto ow_ptr = &ow_info[s_ptr->owner];

	/* Check race */
	if (ow_ptr->races[state][p_ptr->prace / 32] & (1 << p_ptr->prace))
	{
		return true;
	}

	/* Check class */
	if (ow_ptr->classes[state][p_ptr->prace / 32] & (1 << p_ptr->pclass))
	{
		return true;
	}

	/* All failed */
	return false;
}


/*
 * Test if the state accords with the player
 */
bool is_state(store_type const *s_ptr, int state)
{
	if (state == STORE_NORMAL)
	{
		if (is_state_aux(s_ptr, STORE_LIKED)) return false;
		if (is_state_aux(s_ptr, STORE_HATED)) return false;
		return true;
	}

	else
	{
		return (is_state_aux(s_ptr, state));
	}
}


/*
 * Clear the building information
 */
static void clear_bldg(int min_row, int max_row)
{
	int i;


	for (i = min_row; i <= max_row; i++)
	{
		prt("", i, 0);
	}
}


/*
 * Display a building.
 */
void show_building(store_type const *s_ptr)
{
	auto const &ba_info = game->edit_data.ba_info;
	auto const &st_info = game->edit_data.st_info;

	auto st_ptr = &st_info[s_ptr->st_idx];

	for (std::size_t i = 0; i < st_ptr->actions.size(); i++)
	{
		auto ba_ptr = &ba_info[st_ptr->actions[i]];

		byte action_color;
		char buff[20];

		if (ba_ptr->action_restr == 0)
		{
			if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
			                (is_state(s_ptr, STORE_HATED) && (ba_ptr->costs[STORE_HATED] == 0)) ||
			                (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
			{
				action_color = TERM_WHITE;
				buff[0] = '\0';
			}
			else if (is_state(s_ptr, STORE_LIKED))
			{
				action_color = TERM_L_GREEN;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
			}
			else if (is_state(s_ptr, STORE_HATED))
			{
				action_color = TERM_RED;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_HATED]);
			}
			else
			{
				action_color = TERM_YELLOW;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
			}
		}
		else if (ba_ptr->action_restr == 1)
		{
			if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) ||
			                (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0)))
			{
				action_color = TERM_WHITE;
				buff[0] = '\0';
			}
			else if (is_state(s_ptr, STORE_LIKED))
			{
				action_color = TERM_L_GREEN;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
			}
			else if (is_state(s_ptr, STORE_HATED))
			{
				action_color = TERM_L_DARK;
				strnfmt(buff, 20, "(closed)");
			}
			else
			{
				action_color = TERM_YELLOW;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]);
			}
		}
		else
		{
			if (is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0))
			{
				action_color = TERM_WHITE;
				buff[0] = '\0';
			}
			else if (is_state(s_ptr, STORE_LIKED))
			{
				action_color = TERM_L_GREEN;
				strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]);
			}
			else
			{
				action_color = TERM_L_DARK;
				strnfmt(buff, 20, "(closed)");
			}
		}

		char tmp_str[80];

		strnfmt(tmp_str, 80, " %c", ba_ptr->letter);
		c_put_str(TERM_YELLOW, tmp_str, 21 + (i / 2), 17 + (30 * (i % 2)));

		strnfmt(tmp_str, 80, ") %s %s", ba_ptr->name.c_str(), buff);
		c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2)));
	}
}


/*
 * display fruit for dice slots
 */
static void display_fruit(int row, int col, int fruit)
{
	switch (fruit)
	{
	case 0:  /* lemon */
		{
			c_put_str(TERM_YELLOW, "   ####.", row, col);
			c_put_str(TERM_YELLOW, "  #    #", row + 1, col);
			c_put_str(TERM_YELLOW, " #     #", row + 2, col);
			c_put_str(TERM_YELLOW, "#      #", row + 3, col);
			c_put_str(TERM_YELLOW, "#      #", row + 4, col);
			c_put_str(TERM_YELLOW, "#     # ", row + 5, col);
			c_put_str(TERM_YELLOW, "#    #  ", row + 6, col);
			c_put_str(TERM_YELLOW, ".####   ", row + 7, col);
			prt(" Lemon  ", row + 8, col);

			break;
		}

	case 1:  /* orange */
		{
			c_put_str(TERM_ORANGE, "   ##   ", row, col);
			c_put_str(TERM_ORANGE, "  #..#  ", row + 1, col);
			c_put_str(TERM_ORANGE, " #....# ", row + 2, col);
			c_put_str(TERM_ORANGE, "#......#", row + 3, col);
			c_put_str(TERM_ORANGE, "#......#", row + 4, col);
			c_put_str(TERM_ORANGE, " #....# ", row + 5, col);
			c_put_str(TERM_ORANGE, "  #..#  ", row + 6, col);
			c_put_str(TERM_ORANGE, "   ##   ", row + 7, col);
			prt(" Orange ", row + 8, col);

			break;
		}

	case 2:  /* sword */
		{
			c_put_str(TERM_SLATE, "   /\\   ", row, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 1, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 2, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 3, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 4, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 5, col);
			c_put_str(TERM_UMBER, " ###### ", row + 6, col);
			c_put_str(TERM_UMBER, "   ##   ", row + 7, col);
			prt(" Sword  ", row + 8, col);

			break;
		}

	case 3:  /* shield */
		{
			c_put_str(TERM_SLATE, " ###### ", row, col);
			c_put_str(TERM_SLATE, "#      #", row + 1, col);
			c_put_str(TERM_SLATE, "# ++++ #", row + 2, col);
			c_put_str(TERM_SLATE, "# +==+ #", row + 3, col);
			c_put_str(TERM_SLATE, "#  ++  #", row + 4, col);
			c_put_str(TERM_SLATE, " #    # ", row + 5, col);
			c_put_str(TERM_SLATE, "  #  #  ", row + 6, col);
			c_put_str(TERM_SLATE, "   ##   ", row + 7, col);
			prt(" Shield ", row + 8, col);

			break;
		}

	case 4:  /* plum */
		{
			c_put_str(TERM_VIOLET, "   ##   ", row, col);
			c_put_str(TERM_VIOLET, " ###### ", row + 1, col);
			c_put_str(TERM_VIOLET, "########", row + 2, col);
			c_put_str(TERM_VIOLET, "########", row + 3, col);
			c_put_str(TERM_VIOLET, "########", row + 4, col);
			c_put_str(TERM_VIOLET, " ###### ", row + 5, col);
			c_put_str(TERM_VIOLET, "  ####  ", row + 6, col);
			c_put_str(TERM_VIOLET, "   ##   ", row + 7, col);
			prt("  Plum  ", row + 8, col);

			break;
		}

	case 5:  /* cherry */
		{
			c_put_str(TERM_RED, "      ##", row, col);
			c_put_str(TERM_RED, "   ###  ", row + 1, col);
			c_put_str(TERM_RED, "  #..#  ", row + 2, col);
			c_put_str(TERM_RED, "  #..#  ", row + 3, col);
			c_put_str(TERM_RED, " ###### ", row + 4, col);
			c_put_str(TERM_RED, "#..##..#", row + 5, col);
			c_put_str(TERM_RED, "#..##..#", row + 6, col);
			c_put_str(TERM_RED, " ##  ## ", row + 7, col);
			prt(" Cherry ", row + 8, col);

			break;
		}
	}
}


/*
 * gamble_comm
 */
static void gamble_comm(int cmd)
{
	int roll1, roll2, roll3, choice, odds, win;

	s32b wager;

	s32b maxbet;

	s32b oldgold;

	static const char *fruit[6] =
	        {"Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry"
	        };

	char out_val[160], tmp_str[80], again;

	const char *p;


	screen_save();

	if (cmd == BACT_GAMBLE_RULES)
	{
		/* Peruse the gambling help file */
		show_file("gambling.txt", NULL);
	}
	else
	{
		clear_bldg(5, 23);

		/* Set maximum bet */
		if (p_ptr->lev < 10)
			maxbet = (p_ptr->lev * 100);
		else
			maxbet = (p_ptr->lev * 1000);

		/* Get the wager */
		strcpy(out_val, "");
		strnfmt(tmp_str, 80, "Your wager (1-%ld) ? ", maxbet);
		get_string(tmp_str, out_val, 32);

		/* Strip spaces */
		for (p = out_val; *p == ' '; p++);

		wager = atol(p);

		if (wager > p_ptr->au)
		{
			msg_print("Hey! You don't have the gold - get out of here!");
			msg_print(NULL);

			screen_load();
			return;
		}
		else if (wager > maxbet)
		{
			msg_format("I'll take $%ld of that. Keep the rest.", maxbet);
			wager = maxbet;
		}
		else if (wager < 1)
		{
			msg_print("Ok, we'll start with $1.");

			wager = 1;
		}
		msg_print(NULL);
		win = false;
		odds = 0;
		oldgold = p_ptr->au;

		strnfmt(tmp_str, 80, "Gold before game: %9ld", oldgold);
		prt(tmp_str, 20, 2);

		strnfmt(tmp_str, 80, "Current Wager:    %9ld", wager);
		prt(tmp_str, 21, 2);

		do
		{
			switch (cmd)
			{
			case BACT_IN_BETWEEN:  /* Game of In-Between */
				{
					c_put_str(TERM_GREEN, "In Between", 5, 2);
					odds = 3;
					win = false;
					roll1 = randint(10);
					roll2 = randint(10);
					choice = randint(10);
					strnfmt(tmp_str, 80, "Black die: %d       Black Die: %d",
					        roll1, roll2);
					prt(tmp_str, 8, 3);
					strnfmt(tmp_str, 80, "Red die: %d", choice);
					prt(tmp_str, 11, 14);
					if (((choice > roll1) && (choice < roll2)) ||
					                ((choice < roll1) && (choice > roll2)))
						win = true;

					break;
				}
			case BACT_CRAPS:   /* Game of Craps */
				{
					c_put_str(TERM_GREEN, "Craps", 5, 2);
					win = 3;
					odds = 1;
					roll1 = randint(6);
					roll2 = randint(6);
					roll3 = roll1 + roll2;
					choice = roll3;
					strnfmt(tmp_str, 80, "First roll: %d %d    Total: %d", roll1,
					        roll2, roll3);
					prt(tmp_str, 7, 5);
					if ((roll3 == 7) || (roll3 == 11))
						win = true;
					else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12))
						win = false;
					else
					{
						do
						{
							msg_print("Hit any key to roll again");
							msg_print(NULL);
							roll1 = randint(6);
							roll2 = randint(6);
							roll3 = roll1 + roll2;

							strnfmt(tmp_str, 80, "Roll result: %d %d   Total:     %d",
							        roll1, roll2, roll3);
							prt(tmp_str, 8, 5);
							if (roll3 == choice)
								win = true;
							else if (roll3 == 7)
								win = false;
						}
						while ((win != true) && (win != false));
					}

					break;
				}

			case BACT_DICE_SLOTS:  /* The Dice Slots */
				{
					c_put_str(TERM_GREEN, "Dice Slots", 5, 2);
					win = false;
					roll1 = randint(6);
					roll2 = randint(6);
					choice = randint(6);
					strnfmt(tmp_str, 80, "%s %s %s",
					        fruit[roll1 - 1], fruit[roll2 - 1],
					        fruit[choice - 1]);
					prt(tmp_str, 15, 37);
					prt("/--------------------------\\", 7, 2);
					prt("\\--------------------------/", 17, 2);
					display_fruit(8, 3, roll1 - 1);
					display_fruit(8, 12, roll2 - 1);
					display_fruit(8, 21, choice - 1);
					if ((roll1 == roll2) && (roll2 == choice))
					{
						win = true;
						if (roll1 == 1)
							odds = 4;
						else if (roll1 == 2)
							odds = 6;
						else
							odds = roll1 * roll1;
					}
					else if ((roll1 == 6) && (roll2 == 6))
					{
						win = true;
						odds = choice + 1;
					}

					break;
				}
			}

			if (win)
			{
				prt("YOU WON", 16, 37);
				p_ptr->au = p_ptr->au + (odds * wager);
				strnfmt(tmp_str, 80, "Payoff: %d", odds);
				prt(tmp_str, 17, 37);
			}
			else
			{
				prt("You Lost", 16, 37);
				p_ptr->au = p_ptr->au - wager;
				prt("", 17, 37);
			}
			strnfmt(tmp_str, 80, "Current Gold:     %9ld", p_ptr->au);
			prt(tmp_str, 22, 2);
			prt("Again(Y/N)?", 18, 37);
			Term_gotoxy(49, 18);
			again = inkey();
			if (wager > p_ptr->au)
			{
				msg_print("Hey! You don't have the gold - get out of here!");
				msg_print(NULL);
				screen_load();
				return;
			}
		}
		while ((again == 'y') || (again == 'Y'));

		prt("", 18, 37);
		if (p_ptr->au >= oldgold)
			msg_print("You came out a winner! We'll win next time, I'm sure.");
		else
			msg_print("You lost gold! Haha, better head home.");
		msg_print(NULL);
	}

	screen_load();
}


/*
 * inn commands
 * Note that resting for the night was a perfect way to avoid player
 * ghosts in the town *if* you could only make it to the inn in time (-:
 * Now that the ghosts are temporarily disabled in 2.8.X, this function
 * will not be that useful.  I will keep it in the hopes the player
 * ghost code does become a reality again. Does help to avoid filthy urchins.
 * Resting at night is also a quick way to restock stores -KMW- 
 */
static bool inn_comm(int cmd)
{
	/* Is the player a vampire? */
	auto const vampire = ((race_flags_p(PR_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire")));

	switch (cmd)
	{
	case BACT_FOOD:  /* Buy food & drink */
		{
			if (!vampire)
			{
				msg_print("The barkeep gives you some gruel and a beer.");
				msg_print(NULL);
				set_food(PY_FOOD_MAX - 1);
			}
			else
				msg_print("You're a vampire and I don't have any food for you!");

			break;
		}

		/*
		 * I revamped this... Don't know why normal races didn't get
		 * mana regenerated. It is the grand tradition of p&p games -- pelpel
		 */
	case BACT_REST:  /* Rest for the night */
		{
			/* Extract the current time */
			bool const nighttime = ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18));

			/* Normal races rest at night */
			if (!vampire && !nighttime)
			{
				msg_print("The rooms are available only at night.");
				msg_print(NULL);
				return false;
			}

			/* Vampires rest during daytime */
			if (vampire && nighttime)
			{
				msg_print("The rooms are available only during daylight for your kind.");
				msg_print(NULL);
				return false;
			}

			/* Must cure HP draining status first */
			if ((p_ptr->poisoned > 0) || (p_ptr->cut > 0))
			{
				msg_print("You need a healer, not a room.");
				msg_print(NULL);
				msg_print("Sorry, but I don't want anyone dying in here.");
				return false;
			}

			/* Let the time pass XXX XXX XXX */
			if (vampire)
			{
				/* Wait for sunset */
				while ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
				{
					turn += (10L * MINUTE);
				}
			}
			else
			{
				/* Wait for sunrise */
				while ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18))
				{
					turn += (10L * MINUTE);
				}
			}

			/* Regen */
			p_ptr->chp = p_ptr->mhp;
			p_ptr->csp = p_ptr->msp;

			/* Restore status */
			set_blind(0);
			set_confused(0);
			p_ptr->stun = 0;

			/* Message */
			if (vampire) msg_print("You awake refreshed for the new night.");
			else msg_print("You awake refreshed for the new day.");

			/* Dungeon stuff */
			p_ptr->leaving = true;
			p_ptr->oldpx = p_ptr->px;
			p_ptr->oldpy = p_ptr->py;

			break;
		}

	case BACT_RUMORS:  /* Listen for rumors */
		{
			char rumor[80];

			get_rnd_line("rumors.txt", rumor);
			msg_format("%s", rumor);
			msg_print(NULL);

			break;
		}
	}

	return true;
}


/*
 * Display quest information
 */
static void get_questinfo(int questnum)
{
	int i;


	/* Print the quest info */
	prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0);

	prt(quest[questnum].name, 7, 0);

	i = 0;
	while ((i < 10) && (quest[questnum].desc[i][0] != '\0'))
	{
		c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0);
		i++;
	}
}


/*
 * Request a quest from the Lord.
 */
static bool castle_quest(int y, int x)
{
	int plot = 0;

	quest_type *q_ptr;


	clear_bldg(7, 18);

	/* Current plot of the building */
	plot = cave[y][x].special;

	/* Is there a quest available at the building? */
	if ((!plot) || (plots[plot] == QUEST_NULL))
	{
		put_str("I don't have a quest for you at the moment.", 8, 0);
		return false;
	}

	q_ptr = &quest[plots[plot]];

	/* Quest is completed */
	if (q_ptr->status == QUEST_STATUS_COMPLETED)
	{
		/* Rewarded quest */
		q_ptr->status = QUEST_STATUS_FINISHED;

		struct hook_quest_finish_in in = { plots[plot] };
		process_hooks_new(HOOK_QUEST_FINISH, &in, NULL);

		return true;
	}

	/* Quest is still unfinished */
	else if (q_ptr->status == QUEST_STATUS_TAKEN)
	{
		put_str("You have not completed your current quest yet!", 8, 0);
		put_str("Use CTRL-Q to check the status of your quest.", 9, 0);
		put_str("Return when you have completed your quest.", 12, 0);

		return false;
	}
	/* Failed quest */
	else if (q_ptr->status == QUEST_STATUS_FAILED)
	{
		/* Mark quest as done (but failed) */
		q_ptr->status = QUEST_STATUS_FAILED_DONE;

		hook_quest_fail_in in = { plots[plot] };
		process_hooks_new(HOOK_QUEST_FAIL, &in, NULL);

		return false;
	}
	/* No quest yet */
	else if (q_ptr->status == QUEST_STATUS_UNTAKEN)
	{
		struct hook_init_quest_in in = { plots[plot] };
		if (process_hooks_new(HOOK_INIT_QUEST, &in, NULL))
		{
			return false;
		}

		q_ptr->status = QUEST_STATUS_TAKEN;

		/* Assign a new quest */
		get_questinfo(plots[plot]);

		/* Add the hooks */
		quest[plots[plot]].init();

		return true;
	}

	return false;
}

/*
 * Displaying town history -KMW-
 */
static void town_history()
{
	/* Save screen */
	screen_save();

	/* Peruse the building help file */
	show_file("bldg.txt", NULL);

	/* Load screen */
	screen_load();
}


/*
 * compare_weapon_aux2 -KMW-
 */
static void compare_weapon_aux2(object_type *o_ptr, int numblows, int r, int c, int mult, const char *attr, byte color)
{
	char tmp_str[80];

	c_put_str(color, attr, r, c);
	strnfmt(tmp_str, 80, "Attack: %d-%d damage",
	        numblows * ((o_ptr->dd * mult) + o_ptr->to_d),
	        numblows * ((o_ptr->ds * o_ptr->dd * mult) + o_ptr->to_d));
	put_str(tmp_str, r, c + 8);
	r++;
}


/*
 * compare_weapon_aux1 -KMW-
 */
static void compare_weapon_aux1(object_type *o_ptr, int col, int r)
{
	auto const f = object_flags(o_ptr);

	if (f & TR_SLAY_ANIMAL)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Animals:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_EVIL)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Evil:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_UNDEAD)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Undead:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_DEMON)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Demons:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_ORC)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Orcs:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_TROLL)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Trolls:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_GIANT)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Giants:",
				    TERM_YELLOW);
	}
	if (f & TR_SLAY_DRAGON)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Dragons:",
				    TERM_YELLOW);
	}
	if (f & TR_KILL_DRAGON)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 5, "Dragons:",
				    TERM_YELLOW);
	}
	if (f & TR_BRAND_ACID)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Acid:",
				    TERM_RED);
	}
	if (f & TR_BRAND_ELEC)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Elec:",
				    TERM_RED);
	}
	if (f & TR_BRAND_FIRE)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Fire:",
				    TERM_RED);
	}
	if (f & TR_BRAND_COLD)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Cold:",
				    TERM_RED);
	}
	if (f & TR_BRAND_POIS)
	{
		compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Poison:",
				    TERM_RED);
	}
}


/*
 * list_weapon -KMW-
 */
static void list_weapon(object_type *o_ptr, int row, int col)
{
	char o_name[80];

	char tmp_str[80];


	object_desc(o_name, o_ptr, true, 0);
	c_put_str(TERM_YELLOW, o_name, row, col);
	strnfmt(tmp_str, 80, "To Hit: %d   To Damage: %d", o_ptr->to_h, o_ptr->to_d);
	put_str(tmp_str, row + 1, col);
	strnfmt(tmp_str, 80, "Dice: %d   Sides: %d", o_ptr->dd, o_ptr->ds);
	put_str(tmp_str, row + 2, col);
	strnfmt(tmp_str, 80, "Number of Blows: %d", p_ptr->num_blow);
	put_str(tmp_str, row + 3, col);
	c_put_str(TERM_YELLOW, "Possible Damage:", row + 5, col);
	strnfmt(tmp_str, 80, "One Strike: %d-%d damage", o_ptr->dd + o_ptr->to_d,
	        (o_ptr->ds*o_ptr->dd) + o_ptr->to_d);
	put_str(tmp_str, row + 6, col + 1);
	strnfmt(tmp_str, 80, "One Attack: %d-%d damage", p_ptr->num_blow*(o_ptr->dd + o_ptr->to_d),
	        p_ptr->num_blow*(o_ptr->ds*o_ptr->dd + o_ptr->to_d));
	put_str(tmp_str, row + 7, col + 1);
}


/*
 * Select melee weapons
 */
static bool item_tester_hook_melee_weapon(object_type const *o_ptr)
{
	return (wield_slot(o_ptr) == INVEN_WIELD);
}

/*
 * compare_weapons -KMW-
 */
static bool compare_weapons()
{
	int item, i;

	object_type *o1_ptr, *o2_ptr, *orig_ptr;

	clear_bldg(6, 18);

	o1_ptr = NULL;
	o2_ptr = NULL;

	/* Store copy of original wielded weapon in pack slot */
	object_type *i_ptr = &p_ptr->inventory[INVEN_WIELD];
	orig_ptr = &p_ptr->inventory[INVEN_PACK];
	object_copy(orig_ptr, i_ptr);

	i = 6;

	/* Get first weapon */
	if (!get_item(&item,
		      "What is your first melee weapon? ",
		      "You have nothing to compare.",
		      (USE_EQUIP | USE_INVEN),
		      item_tester_hook_melee_weapon))
	{
		object_wipe(orig_ptr);
		return false;
	}

	/* Get the item (in the pack) */
	if (item >= 0)
		o1_ptr = &p_ptr->inventory[item];

	/* Get second weapon */
	int item2;
	if (!get_item(&item2,
		      "What is your second melee weapon? ",
		      "You have nothing to compare.",
		      (USE_EQUIP | USE_INVEN),
		      item_tester_hook_melee_weapon))
	{
		object_wipe(orig_ptr);
		return false;
	}

	/* Get the item (in the pack) */
	if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2];

	put_str("Based on your current abilities, here is what your weapons will do", 4, 2);

	i_ptr = &p_ptr->inventory[INVEN_WIELD];
	object_copy(i_ptr, o1_ptr);
	calc_bonuses(true);

	list_weapon(o1_ptr, i, 2);
	compare_weapon_aux1(o1_ptr, 2, i + 8);

	i_ptr = &p_ptr->inventory[INVEN_WIELD];
	if (item2 == INVEN_WIELD)
		object_copy(i_ptr, orig_ptr);
	else
		object_copy(i_ptr, o2_ptr);
	calc_bonuses(true);

	list_weapon(o2_ptr, i, 40);
	compare_weapon_aux1(o2_ptr, 40, i + 8);

	i_ptr = &p_ptr->inventory[INVEN_WIELD];
	object_copy(i_ptr, orig_ptr);
	calc_bonuses(true);

	object_wipe(orig_ptr);

	put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0);

	return true;
}


/*
 * general all-purpose fixing routine for items from building personnel
 * sharpen arrows, repair armor, repair weapon
 * -KMW-
 */
static bool fix_item(int istart, int iend, int ispecific, bool iac)
{
	int i;

	int j = 9;

	int maxenchant = (p_ptr->lev / 5);

	object_type *o_ptr;

	char out_val[80], tmp_str[80];

	bool repaired = false;

	clear_bldg(5, 18);
	strnfmt(tmp_str, 80, "  Based on your skill, we can improve up to +%d", maxenchant);
	prt(tmp_str, 5, 0);
	prt("Status", 7, 30);

	for (i = istart; i <= iend; i++)
	{
		o_ptr = &p_ptr->inventory[i];
		if (ispecific > 0)
		{
			if (o_ptr->tval != ispecific)
				continue;
		}

		if (o_ptr->tval)
		{
			object_desc(tmp_str, o_ptr, false, 1);

			if (o_ptr->name1 && o_ptr->identified)
				strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str);
			else if (o_ptr->name1)
				strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
			else
			{
				if ((iac) && (o_ptr->to_a <= -3))
				{
					strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
				}
				else if ((iac) && (o_ptr->to_a < maxenchant))
				{
					o_ptr->to_a++;
					strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a);
					repaired = true;
				}
				else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3)))
				{
					strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
				}
				/* Sharpen a weapon */
				else if ((!iac) && ((o_ptr->to_h < maxenchant) ||
				                    (o_ptr->to_d < maxenchant)))
				{
					if (o_ptr->to_h < maxenchant)
						o_ptr->to_h++;
					if (o_ptr->to_d < maxenchant)
						o_ptr->to_d++;
					strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str,
					        o_ptr->to_h, o_ptr->to_d);
					repaired = true;
				}
				else
					strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
			}
			prt(out_val, j++, 0);
		}
	}

	if (!repaired)
	{
		msg_print("You don't have anything appropriate.");
		msg_print(NULL);
	}
	else
	{
		msg_print("Press the spacebar to continue");
		msg_print(NULL);

		/* Window stuff */
		p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
		p_ptr->update |= (PU_BONUS);
	}
	clear_bldg(5, 18);

	return repaired;
}


/*
 * Execute a building command
 */
bool bldg_process_command(const store_type *s_ptr, store_action_type const *ba_ptr)
{
	int bact = ba_ptr->action;

	int bcost;

	bool paid = false;

	bool recreate = false;


	if (is_state(s_ptr, STORE_LIKED))
	{
		bcost = ba_ptr->costs[STORE_LIKED];
	}
	else if (is_state(s_ptr, STORE_HATED))
	{
		bcost = ba_ptr->costs[STORE_HATED];
	}
	else
	{
		bcost = ba_ptr->costs[STORE_NORMAL];
	}

	/* action restrictions */
	if (((ba_ptr->action_restr == 1) && (is_state(s_ptr, STORE_LIKED))) ||
	    ((ba_ptr->action_restr == 2) && (!is_state(s_ptr, STORE_LIKED))))
	{
		msg_print("You have no right to choose that!");
		msg_print(NULL);
		return false;
	}

	/* check gold */
	if (bcost > p_ptr->au)
	{
		msg_print("You do not have the gold!");
		msg_print(NULL);
		return false;
	}

	switch (bact)
	{
	case BACT_TOWN_HISTORY:
		{
			town_history();
			break;
		}

	case BACT_RACE_LEGENDS:
		{
			race_legends();
			break;
		}

	case BACT_QUEST1:
		{
			int y = 1;
			int x = 1;
			bool ok = false;

			while ((x < cur_wid - 1) && !ok)
			{
				y = 1;
				while ((y < cur_hgt - 1) && !ok)
				{
					/* Found the location of the quest info ? */
					if (bact - BACT_QUEST1 + FEAT_QUEST1 == cave[y][x].feat)
					{
						/* Stop the loop */
						ok = true;
					}
					y++;
				}
				x++;
			}

			if (ok)
			{
				recreate = castle_quest(y - 1, x - 1);
			}
			else
			{
				msg_format("ERROR: no quest info feature found: %d", bact - BACT_QUEST1 + FEAT_QUEST1);
			}
			break;
		}

	case BACT_KING_LEGENDS:
		{
			show_highclass(building_loc);
			break;
		}

	case BACT_IN_BETWEEN:
	case BACT_CRAPS:
	case BACT_DICE_SLOTS:
	case BACT_GAMBLE_RULES:
		{
			gamble_comm(bact);
			break;
		}

	case BACT_REST:
	case BACT_RUMORS:
	case BACT_FOOD:
		{
			paid = inn_comm(bact);
			break;
		}

	case BACT_COMPARE_WEAPONS:
		{
			paid = compare_weapons();
			break;
		}

	case BACT_ENCHANT_WEAPON:
		{
			paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, false);
			break;
		}

	case BACT_ENCHANT_ARMOR:
		{
			paid = fix_item(INVEN_BODY, INVEN_FEET, 0, true);
			break;
		}

		/* needs work */
	case BACT_RECHARGE:
		{
			if (recharge(80))
			{
				paid = true;
			}
			break;
		}

		/* needs work */
	case BACT_STAR_HEAL:
		{
			hp_player(200);
			set_poisoned(0);
			set_blind(0);
			set_confused(0);
			set_cut(0);
			set_stun(0);
			if (p_ptr->black_breath)
			{
				msg_print("The hold of the Black Breath on you is broken!");
				p_ptr->black_breath = false;
			}

			paid = true;
			break;
		}

		/* needs work */
	case BACT_HEALING:
		{
			hp_player(200);
			set_poisoned(0);
			set_blind(0);
			set_confused(0);
			set_cut(0);
			set_stun(0);

			paid = true;
			break;
		}

		/* needs work */
	case BACT_RESTORE:
		{
			if (do_res_stat(A_STR, true)) paid = true;
			if (do_res_stat(A_INT, true)) paid = true;
			if (do_res_stat(A_WIS, true)) paid = true;
			if (do_res_stat(A_DEX, true)) paid = true;
			if (do_res_stat(A_CON, true)) paid = true;
			if (do_res_stat(A_CHR, true)) paid = true;
			break;
		}

	case BACT_ENCHANT_ARROWS:
		{
			paid = fix_item(0, INVEN_WIELD, TV_ARROW, false);
			break;
		}

	case BACT_ENCHANT_BOW:
		{
			paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, false);
			break;
		}

	case BACT_RECALL:
		{
			p_ptr->word_recall = 1;
			msg_print("The air about you becomes charged...");

			paid = true;
			break;
		}

	case BACT_TELEPORT_LEVEL:
		{
			if (reset_recall(false))
			{
				p_ptr->word_recall = 1;
				msg_print("The air about you becomes charged...");

				paid = true;
			}
			break;
		}

	case BACT_MIMIC_NORMAL:
		{
			set_mimic(0, 0, 0);
			paid = true;
			break;
		}

	case BACT_DIVINATION:
		{
			bool found = false;

			for (int count = 0; count < 1000; count++)
			{
				int i = rand_int(MAX_FATES);
				if (!fates[i].fate) continue;
				if (fates[i].know) continue;
				msg_print("You know a little more of your fate.");

				fates[i].know = true;

				found = true;
				break;
			}

			if (!found)
			{
				msg_print("Well, you have no fate, but I'll keep your money anyway!");
			}

			paid = true;
			break;

		}

	case BACT_BUY:
		{
			store_purchase();
			break;
		}

	case BACT_SELL:
		{
			store_sell();
			break;
		}

	case BACT_EXAMINE:
		{
			store_examine();
			break;
		}

	case BACT_STEAL:
		{
			store_stole();
			break;
		}

	case BACT_DROP_ITEM:
	{
		quest_bounty_drop_item();
		break;
	}

	case BACT_GET_ITEM:
	{
		quest_bounty_get_item();
		break;
	}

	case BACT_LIBRARY_QUEST:
	{
		quest_library_building(&paid, &recreate);
		break;
	}

	case BACT_FIREPROOF_QUEST:
	{
		quest_fireproof_building(&paid, &recreate);
		break;
	}

	case BACT_EREBOR_KEY:
	{
		msg_print("You will need Thorin's Key and Thrain's Map"
			  " to get anywhere in Erebor. One may be found"
			  " in the Barrow-Downs. The other, in Mirkwood.");
		break;
	}

	default:
		printf("Unknown building action %d\n", static_cast<int>(bact));
		break;
	}

	if (paid)
	{
		p_ptr->au -= bcost;

		/* Display the current gold */
		store_prt_gold();
	}

	return recreate;
}


/*
 * Enter quest level
 */
void enter_quest()
{
	if (!(cave[p_ptr->py][p_ptr->px].feat == FEAT_QUEST_ENTER))
	{
		msg_print("You see no quest level here.");
		return;
	}
	else
	{
		/* Player enters a new quest */
		p_ptr->oldpy = p_ptr->py;
		p_ptr->oldpx = p_ptr->px;

		leaving_quest = p_ptr->inside_quest;

		p_ptr->inside_quest = cave[p_ptr->py][p_ptr->px].special;
		dun_level = 1;
		p_ptr->leaving = true;
		p_ptr->oldpx = p_ptr->px;
		p_ptr->oldpy = p_ptr->py;
	}
}
