empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: act.other.c                                    EmpireMUD AD 1.0 *
*  Usage: Miscellaneous player-level commands                             *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#define __ACT_OTHER_C__

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "empire.h"
#include "skills.h"
#include "vnums.h"


ACMD(do_afk) {
	char **msg;
	bool change = FALSE;

	if (IS_NPC(ch))
		return;

	skip_spaces(&argument);

	if (IS_AFK(ch) && !*argument) {
		(ch)->player_specials->isafk = FALSE;
		send_to_char("You announce that you are no longer AFK.\r\n", ch);
		act("$n announces that $e is no longer away from $s keyboard.", TRUE, ch, 0, 0, TO_ROOM);
		if (AFK_MSGS(ch))
			send_to_char("Type MESSAGES to receive missed tells.\r\n", ch);
		return;
		}

	if (IS_NPC(ch))
		return;

	if (IS_AFK(ch))
		change = TRUE;
	msg = &(AFK_MSG(ch));

	if (*msg)
		free(*msg);

	if (!*argument)
		*msg = NULL;
	else
		*msg = str_dup(argument);

	(ch)->player_specials->isafk = TRUE;
	if (!change) {
		send_to_char("You announce that you are going AFK.\r\n", ch);
		act("$n announces that $e is going to be away from $s keyboard.", FALSE, ch, 0, 0, TO_ROOM);
		if (*argument) {
			sprintf(buf, "$e says, '%s'", argument);
			act(buf, FALSE, ch, 0, 0, TO_ROOM);
			}
		}
	else
		send_to_char("Your AFK message has been changed.\r\n", ch);
	}

ACMD(do_messages) {
	char **msg;
	if (!AFK_MSGS(ch)) {
		send_to_char("You have no new messages.\r\n", ch);
		return;
		}
	
	page_string(ch->desc, AFK_MSGS(ch), 1);

	msg = &(AFK_MSGS(ch));
	if (*msg)
		free(*msg);
	if (!*argument)
		*msg = NULL;
	}


/* Either displays current prompt, or sets one */
ACMD(do_prompt) {
	char temp[MAX_INPUT_LENGTH];
	int i, j;

	skip_spaces(&argument);

	if (!*argument) {
		if (GET_PROMPT(ch)) {
			strcpy(buf, GET_PROMPT(ch));
			for (i = 0, j = 0; i <= strlen(buf); i++)
				if (buf[i] != '&')
					temp[i+j] = buf[i];
				else {
					temp[i+j] = temp[i+j+1] = buf[i];
					j++;
					}
			}

		sprintf(buf, "Your prompt is currently: %s\r\n", (GET_PROMPT(ch) ? temp : "n/a"));
		send_to_char(buf, ch);
		return;
		}

	delete_doubledollar(argument);

	if (strlen(argument) > 120)
		argument[120 - 1] = '\0';

	if (!str_cmp(argument, "off")) {
		if (GET_PROMPT(ch))
			free(GET_PROMPT(ch));
		GET_PROMPT(ch) = NULL;
		send_to_char(OK, ch);
		}
	else {
		if (GET_PROMPT(ch))
			free(GET_PROMPT(ch));
		GET_PROMPT(ch) = str_dup(argument);

		for (i = 0, j = 0; i <= strlen(argument); i++)
			if (argument[i] != '&')
				temp[i+j] = argument[i];
			else {
				temp[i+j] = temp[i+j+1] = argument[i];
				j++;
				}

		sprintf(buf, "Okay, set your prompt to: %s\r\n", temp);
		send_to_char(buf, ch);
		}
	}


ACMD(do_quit) {
	ACMD(do_vc);
	void Crash_rentsave(Creature ch);
	void die(Creature ch);
	Descr d, next_d;

	if (IS_NPC(ch) || !ch->desc)
		return;

	if (subcmd != SCMD_QUIT && GET_LEVEL(ch) < LVL_GOD)
		msg_to_char(ch, "You have to type quit--no less, to quit!\r\n");
	else if (GET_POS(ch) == POS_FIGHTING || FIGHTING(ch))
		msg_to_char(ch, "No way!  You're fighting for your life!\r\n");
	else if (GET_QUIT_TIMER(ch) > 0 && !IS_IMMORTAL(ch))
		msg_to_char(ch, "You can't quit so soon after fighting!\r\n");
	else if (GET_FED_ON_BY(ch))
		msg_to_char(ch, "You can't quit with fangs in your neck!\r\n");
	else if (GET_FEEDING_FROM(ch))
		msg_to_char(ch, "You can't quit while drinking blood!\r\n");
	else if (GET_POS(ch) < POS_STUNNED) {
		send_to_char("You die before your time...\r\n", ch);
		die(ch);
		}
	else {
		if (!GET_INVIS_LEV(ch))
			act("$n has left the game.", TRUE, ch, 0, 0, TO_ROOM);
		syslog(GET_INVIS_LEV(ch), TRUE, "%s has quit the game.", GET_NAME(ch));
		if (GET_INVIS_LEV(ch) == 0)
			mortlog(ch, "%s has left the game", PERS(ch, ch, 1));
		send_to_char("Goodbye, friend.. Come back soon!\r\n", ch);

		/* Close out of the virtual channel, if a member of one */
		if (GET_VC(ch) > 0)
			do_vc(ch, "close", 0, 0);

		/*
		 * kill off all sockets connected to the same player as the one who is
		 * trying to quit.  Helps to maintain sanity as well as prevent duping.
		 */
		for (d = descriptor_list; d; d = next_d) {
			next_d = d->next;
			if (d == ch->desc)
				continue;
			if (d->character && (GET_IDNUM(d->character) == GET_IDNUM(ch)))
				STATE(d) = CON_DISCONNECT;
			}

		Crash_rentsave(ch);
		SAVE_CHAR(ch);
		extract_char(ch);
		}
	}


ACMD(do_save) {
	void write_aliases(Creature ch);

	if (IS_NPC(ch) || !ch->desc)
		return;

	if (cmd)
		msg_to_char(ch, "Saving %s.\r\n", GET_NAME(ch));

	write_aliases(ch);
	SAVE_CHAR(ch);
	Crash_crashsave(ch);
	}


ACMD(do_visible) {
	void perform_immort_vis(Creature ch);

	if (GET_LEVEL(ch) >= LVL_GOD) {
		perform_immort_vis(ch);
		return;
		}

	if (SHOULD_APPEAR(ch))
		appear(ch);
	else
		send_to_char("You are already visible.\r\n", ch);
	}


ACMD(do_title) {
	void set_title(Creature ch, char *title);

	skip_spaces(&argument);
	delete_doubledollar(argument);

	if (IS_NPC(ch))
		send_to_char("Your title is fine... go away.\r\n", ch);
	else if (PLR_FLAGGED(ch, PLR_NOTITLE))
		send_to_char("You can't title yourself -- you shouldn't have abused it!\r\n", ch);
	else if (strstr(argument, "(") || strstr(argument, ")") || strstr(argument, "%"))
		send_to_char("Titles can't contain the (, ), or % characters.\r\n", ch);
	else if (strlen(argument) > MAX_TITLE_LENGTH) {
		sprintf(buf, "Sorry, titles can't be longer than %d characters.\r\n", MAX_TITLE_LENGTH);
		send_to_char(buf, ch);
		}
	else {
		set_title(ch, argument);
		msg_to_char(ch, "Okay, you're now %s%s.\r\n", PERS(ch, ch, 1), GET_TITLE(ch));
		}
	}


int perform_party(Creature ch, Creature vict) {
	if (AFF_FLAGGED(vict, AFF_PARTY) || !CAN_SEE(ch, vict))
		return (0);

	SET_BIT(AFF_FLAGS(vict), AFF_PARTY);
	if (ch != vict)
   		act("$N is now a member of your party.", FALSE, ch, 0, vict, TO_CHAR);
	act("You are now a member of $n's party.", FALSE, ch, 0, vict, TO_VICT);
	act("$N is now a member of $n's party.", FALSE, ch, 0, vict, TO_NOTVICT);
	return (1);
	}


void print_party(Creature ch) {
	Creature k;
	struct follow_type *f;

	if (!AFF_FLAGGED(ch, AFF_PARTY))
		send_to_char("But you are not the member of a party!\r\n", ch);
	else {
		send_to_char("Your party consists of:\r\n", ch);

		k = (ch->master ? ch->master : ch);

		if (AFF_FLAGGED(k, AFF_PARTY))
			act("     $N (Leader of party)", FALSE, ch, 0, k, TO_CHAR);

		for (f = k->followers; f; f = f->next) {
			if (!AFF_FLAGGED(f->follower, AFF_PARTY))
				continue;

			act("     $N", FALSE, ch, 0, f->follower, TO_CHAR);
			}
		}
	}


ACMD(do_party) {
	Creature vict;
	struct follow_type *f;
	int found;

	one_argument(argument, buf);

	if (!*buf) {
		print_party(ch);
		return;
		}

	if (ch->master) {
		act("You can't allow others to join a party you don't even lead!", FALSE, ch, 0, 0, TO_CHAR);
		return;
		}

	if (!str_cmp(buf, "all")) {
		perform_party(ch, ch);
		for (found = 0, f = ch->followers; f; f = f->next)
			found += perform_party(ch, f->follower);
		if (!found)
			send_to_char("Everyone following you is already in your party.\r\n", ch);
		return;
		}

	if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM)))
		send_to_char(NOPERSON, ch);
	else if ((vict->master != ch) && (vict != ch))
		act("$N must follow you to enter your party.", FALSE, ch, 0, vict, TO_CHAR);
	else {
		if (!AFF_FLAGGED(vict, AFF_PARTY))
			perform_party(ch, vict);
		else {
			if (ch != vict)
				act("$N is no longer a member of your party.", FALSE, ch, 0, vict, TO_CHAR);
			act("You have been kicked out of $n's party!", FALSE, ch, 0, vict, TO_VICT);
			act("$N has been kicked out of $n's party!", FALSE, ch, 0, vict, TO_NOTVICT);
			REMOVE_BIT(AFF_FLAGS(vict), AFF_PARTY);
			}
		}
	}


ACMD(do_unparty) {
	struct follow_type *f, *next_fol;
	Creature tch;

	one_argument(argument, buf);

	if (!*buf) {
		if (ch->master || !(AFF_FLAGGED(ch, AFF_PARTY))) {
			send_to_char("But you lead no party!\r\n", ch);
			return;
			}
		sprintf(buf2, "%s has disbanded the party.\r\n", PERS(ch, ch, 0));
		for (f = ch->followers; f; f = next_fol) {
			next_fol = f->next;
			if (AFF_FLAGGED(f->follower, AFF_PARTY)) {
				REMOVE_BIT(AFF_FLAGS(f->follower), AFF_PARTY);
				send_to_char(buf2, f->follower);
				if (!AFF_FLAGGED(f->follower, AFF_CHARM))
					stop_follower(f->follower);
				}
			}

		REMOVE_BIT(AFF_FLAGS(ch), AFF_PARTY);
		send_to_char("You disband the party.\r\n", ch);
		return;
		}
	if (!(tch = get_char_vis(ch, buf, FIND_CHAR_ROOM))) {
		send_to_char("There is no such person!\r\n", ch);
		return;
		}
	if (tch->master != ch) {
		send_to_char("That person is not following you!\r\n", ch);
		return;
		}

	if (!AFF_FLAGGED(tch, AFF_PARTY)) {
		send_to_char("That person isn't in your party.\r\n", ch);
		return;
		}

	REMOVE_BIT(AFF_FLAGS(tch), AFF_PARTY);

	act("$N is no longer a member of your party.", FALSE, ch, 0, tch, TO_CHAR);
	act("You have been kicked out of $n's party!", FALSE, ch, 0, tch, TO_VICT);
	act("$N has been kicked out of $n's party!", FALSE, ch, 0, tch, TO_NOTVICT);
 
	if (!AFF_FLAGGED(tch, AFF_CHARM))
		stop_follower(tch);
	}


ACMD(do_report) {
	extern const char *health_levels[];
	extern const char *move_levels[];

	Creature k;
	struct follow_type *f;

	if (!AFF_FLAGGED(ch, AFF_PARTY)) {
		send_to_char("But you are not a member of any party!\r\n", ch);
		return;
		}
	sprintf(buf, "$n reports that $e is %s and feels %s.", health_levels[GET_DAMAGE(ch)], move_levels[(GET_MOVE(ch) * 100 / GET_MAX_MOVE(ch)) / 10]);

	k = (ch->master ? ch->master : ch);

	for (f = k->followers; f; f = f->next) 
		if (AFF_FLAGGED(f->follower, AFF_PARTY) && f->follower != ch)
			act(buf, FALSE, ch, 0, f->follower, TO_VICT);
	if (k != ch)
		act(buf, FALSE, ch, 0, k, TO_VICT);
	send_to_char("You report to the party.\r\n", ch);
	}


ACMD(do_gen_write) {
	extern int max_filesize;
	FILE *fl;
	char *tmp;
	const char *filename;
	char *name;
	struct stat fbuf;
	time_t ct;

	switch (subcmd) {
		case SCMD_BUG:		filename = BUG_FILE;	name = "bug";		break;
		case SCMD_TYPO:		filename = TYPO_FILE;	name = "typo";		break;
		case SCMD_IDEA:		filename = IDEA_FILE;	name = "idea";		break;
		default:														return;
		}

	ct = time(0);
	tmp = asctime(localtime(&ct));

	if (IS_NPC(ch)) {
		send_to_char("Monsters can't have ideas - Go away.\r\n", ch);
		return;
		}

	skip_spaces(&argument);
	delete_doubledollar(argument);

	if (!*argument) {
		send_to_char("That must be a mistake...\r\n", ch);
		return;
		}
	syslog(GET_INVIS_LEV(ch), FALSE, "%s %s: %s", GET_NAME(ch), name, argument);

	if (stat(filename, &fbuf) < 0) {
		perror("SYSERR: Can't stat() file");
		return;
		}
	if (fbuf.st_size >= max_filesize) {
		send_to_char("Sorry, the file is full right now.. try again later.\r\n", ch);
		return;
		}
	if (!(fl = fopen(filename, "a"))) {
		perror("SYSERR: do_gen_write");
		send_to_char("Could not open the file.  Sorry.\r\n", ch);
		return;
		}
	fprintf(fl, "%-8s (%6.6s) %s\n", GET_NAME(ch), (tmp + 4), argument);
	fclose(fl);
	send_to_char("Okay.  Thanks!\r\n", ch);
	}


#define TOG_OFF 0
#define TOG_ON  1

#define PRF_TOG_CHK(ch,flag) ((TOGGLE_BIT(PRF_FLAGS(ch), (flag))) & (flag))

ACMD(do_gen_tog) {
	long result;

	const char *tog_messages[][2] = {
		{ "You will no longer see mortal logs.\r\n",
			"You will now see mortal logs.\r\n" },
		{ "Compact mode off.\r\n",
			"Compact mode on.\r\n" },
		{ "You can now hear tells.\r\n",
			"You are now deaf to tells.\r\n" },
		{ "You can now hear shouts.\r\n",
			"You are now deaf to shouts.\r\n" },
		{ "You can now hear gossip.\r\n",
			"You are now deaf to gossip.\r\n" },
		{ "You can now hear the out-of-character channel.\r\n",
			"You are now deaf to the out-of-character channel.\r\n" },
		{ "You can now hear the Wiz-channel.\r\n",
			"You are now deaf to the Wiz-channel.\r\n" },
		{ "You will now have your communication repeated.\r\n",
			"You will no longer have your communication repeated.\r\n" },
		{ "HolyLight mode off.\r\n",
			"HolyLight mode on.\r\n" },
		{ "You will now see normal map colors.\r\n",
			"You will now see political colors on the map.\r\n" },
		{ "You will no longer see players on the map.\r\n",
			"You will now see other players on the map.\r\n" },
		{ "Your color is now off.\r\n",
			"Your &1color&0 is now on.\r\n" },
		{ "The screen will now clear when you look.\r\n",
			"The screen will no longer clear when you look.\r\n" },
		{ "Your e-mail address will now be shown.\r\n",
			"Your e-mail address is now hidden.\r\n" },
		{ "You will now see color on the world map.\r\n",
			"You will no longer see colors on the world map.\r\n" },
		{ "You will no longer automatically kill other people in combat.\r\n",
			"You will now automatically kill other people in combat.\r\n" },
		{ "Long messages will now be paged instead of scrolled.\r\n",
			"Long messages will now be scrolled instead of paged.\r\n" },
		{ "You will now see the full map and room descriptions.\r\n",
			"You will now see the short map and no room descriptions.\r\n" },
		{ "You will no longer allow yourself to be bit by vampires.\r\n",
			"You now allow vampires to bite you freely.\r\n" },
		{ "You will no longer accept blood freely.\r\n",
			"You now allow yourself to be fed blood from vampires.\r\n" },
		{ "You will no longer allow yourself to be taught disciplines.\r\n",
			"You will now allow yourself to be taught disciplines.\r\n" },
		{ "You are no longer role-playing.\r\n",
			"You are now role-playing.\r\n"	},
		};


	if (IS_NPC(ch))
		return;

	switch (subcmd) {
		case SCMD_RP:			result = PRF_TOG_CHK(ch, PRF_RP);			break;
		case SCMD_NOBITE:		result = PRF_TOG_CHK(ch, PRF_BITEABLE);		break;
		case SCMD_NOFEED:		result = PRF_TOG_CHK(ch, PRF_FEEDABLE);		break;
		case SCMD_NOTEACH:		result = PRF_TOG_CHK(ch, PRF_TEACHABLE);	break;
		case SCMD_BRIEF:		result = PRF_TOG_CHK(ch, PRF_BRIEF);		break;
		case SCMD_SCROLLING:	result = PRF_TOG_CHK(ch, PRF_SCROLLING);	break;
		case SCMD_AUTOKILL:		result = PRF_TOG_CHK(ch, PRF_AUTOKILL);		break;
		case SCMD_NOCLEAR:		result = PRF_TOG_CHK(ch, PRF_NOCLEAR);		break;
		case SCMD_COLOR:		result = PRF_TOG_CHK(ch, PRF_COLOR);		break;
		case SCMD_NOMAPCOL:		result = PRF_TOG_CHK(ch, PRF_NOMAPCOL);		break;
		case SCMD_MAPPC:		result = PRF_TOG_CHK(ch, PRF_MAPPC);		break;
		case SCMD_MORTLOG:		result = PRF_TOG_CHK(ch, PRF_MORTLOG);		break;
		case SCMD_POLITICAL:	result = PRF_TOG_CHK(ch, PRF_POLITICAL);	break;
		case SCMD_COMPACT:		result = PRF_TOG_CHK(ch, PRF_COMPACT);		break;
		case SCMD_NOTELL:		result = PRF_TOG_CHK(ch, PRF_NOTELL);		break;
		case SCMD_DEAF:			result = PRF_TOG_CHK(ch, PRF_DEAF);			break;
		case SCMD_NOGOSSIP:		result = PRF_TOG_CHK(ch, PRF_NOGOSS);		break;
		case SCMD_NOOOC:		result = PRF_TOG_CHK(ch, PRF_NOOOC);		break;
		case SCMD_NOWIZ:		result = PRF_TOG_CHK(ch, PRF_NOWIZ);		break;
		case SCMD_NOREPEAT:		result = PRF_TOG_CHK(ch, PRF_NOREPEAT);		break;
		case SCMD_HOLYLIGHT:	result = PRF_TOG_CHK(ch, PRF_HOLYLIGHT);	break;
		case SCMD_HIDEEMAIL:	result = PRF_TOG_CHK(ch, PRF_HIDEEMAIL);	break;
		default:
			log("SYSERR: Unknown subcmd %d in do_gen_toggle.", subcmd);
			return;
		}

	if (result)		send_to_char(tog_messages[subcmd][TOG_ON], ch);
	else			send_to_char(tog_messages[subcmd][TOG_OFF], ch);

	SAVE_CHAR(ch);
	}


ACMD(do_attach) {
	char arg1[MAX_INPUT_LENGTH];
	Object obj1, obj2, new;

	two_arguments(argument, arg, arg1);
	if (!*arg || !*arg1) {
		send_to_char("Attach what to what?\r\n", ch);
		return;
		}

	if (!(obj1 = get_obj_in_list_vis(ch, arg, ch->carrying))) {
		msg_to_char(ch, "You don't seem to have a '%s'.\r\n", arg);
		return;
		}

	if (!(obj2 = get_obj_in_list_vis(ch, arg1, ch->carrying))) {
		msg_to_char(ch, "You don't seem to have a '%s'.\r\n", arg1);
		return;
		}

	if (obj1 == obj2) {
		msg_to_char(ch, "You can't attach that to itself!\r\n");
		return;
		}

	switch (GET_OBJ_VNUM(obj1)) {
		case o_HANDLE:
			if (GET_OBJ_VNUM(obj2) == o_HANDAXE) {
				act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR);
				act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM);
				obj_to_char(read_object(o_AXE, VIRTUAL), ch);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			if (GET_OBJ_VNUM(obj2) == o_SPEARHEAD) {
				act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR);
				act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM);
				obj_to_char(read_object(o_SPEAR, VIRTUAL), ch);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			break;
		case o_HANDAXE:
			if (GET_OBJ_VNUM(obj2) == o_HANDLE) {
				act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR);
				act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM);
				obj_to_char(read_object(o_AXE, VIRTUAL), ch);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			break;
		case o_ROCK:
			if (GET_OBJ_VNUM(obj2) == o_FLINT) {
				obj_to_char((new = read_object(o_FLINT_SET, VIRTUAL)), ch);
				act("You now have $p.", FALSE, ch, new, 0, TO_CHAR);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			break;
		case o_FLINT:
			if (GET_OBJ_VNUM(obj2) == o_ROCK) {
				obj_to_char((new = read_object(o_FLINT_SET, VIRTUAL)), ch);
				act("You now have $p.", FALSE, ch, new, 0, TO_CHAR);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			break;
		case o_SPEARHEAD:
			if (GET_OBJ_VNUM(obj2) == o_HANDLE) {
				act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR);
				act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM);
				obj_to_char(read_object(o_SPEAR, VIRTUAL), ch);
				extract_obj(obj1);
				extract_obj(obj2);
				return;
				}
			break;
		}

	strcpy(buf1, GET_OBJ_DESC(obj2, ch, 1));
	sprintf(buf, "You can't attach %s to %s.\r\n", GET_OBJ_DESC(obj1, ch, 1), buf1);
	send_to_char(buf, ch);
	}


/*
 * do_confirm:
 *   This function both allows players to speed up a reboot, and to cause the reboot
 *   if all players have confirmed.  Because of this second function, it is called
 *   by update_reboot() every tick to make sure that everything is okay.  Because it
 *   is called by that function, it must be made to run WITHOUT (ch). -pc 10/21/2k
 */
ACMD(do_confirm) {
	ACMD(do_reboot);
	Descr d;
	bool found = FALSE;

	if (ch) {
		if (IS_NPC(ch))
			return;

		if (GET_POS(ch) == POS_FIGHTING) {
			msg_to_char(ch, "How can you be ready?  You're fighting for your life!\r\n");
			return;
			}

		if (reboot.time == -1) {
			msg_to_char(ch, "No reboot has been set!\r\n");
			return;
			}

		if (REBOOT_CONF(ch))
			msg_to_char(ch, "You've already confirmed that you're ready for the reboot.\r\n");
		else {
			REBOOT_CONF(ch) = TRUE;
			msg_to_char(ch, "You have confirmed that you're ready for the reboot.\r\n");
			}
		}

	for (d = descriptor_list; d; d = d->next)
		if (STATE(d) != CON_PLAYING)
			found = TRUE;
		else if (IS_NPC(d->character))
			continue;
		else if (!REBOOT_CONF(d->character))
			found = TRUE;
	if (found)
		return;
	do_reboot(NULL, "", 0, reboot.type);
	}


ACMD(do_douse) {
	Object obj;
	byte amount;
	room_rnum room = HOME_ROOM(ch->in_room);

	for (obj = ch->carrying; obj && !(GET_OBJ_TYPE(obj) == ITEM_DRINKCON && GET_OBJ_VAL(obj, 1)); obj = obj->next_content);

	if (ch->in_room == NOWHERE)
		msg_to_char(ch, "Unexpected error in douse.\r\n");
	else if (SECT(ch->in_room) != SECT_BUILDING && SECT(ch->in_room) != SECT_INSIDE)
		msg_to_char(ch, "You can't really douse a fire here.\r\n");
	else if (!world[room].burning)
		msg_to_char(ch, "There's no fire here!\r\n");
	else if (!obj)
		msg_to_char(ch, "What do you plan to douse the fire with?\r\n");
	else if (GET_OBJ_VAL(obj, 2) != LIQ_WATER)
		msg_to_char(ch, "Only water will douse a fire!\r\n");
	else {
		amount = MIN(12, GET_OBJ_VAL(obj, 1));
		GET_OBJ_VAL(obj, 1) -= amount;
		world[room].burning = MIN(120, world[room].burning + amount);
		act("You throw some water from $p onto the flames!", FALSE, ch, obj, 0, TO_CHAR);
		act("$n throws some water from $p onto the flames!", FALSE, ch, obj, 0, TO_CHAR);
		if (world[room].burning == 120) {
			act("The flames have been extinguished!", FALSE, ch, 0, 0, TO_CHAR | TO_ROOM);
			world[room].burning = 0;
			}
		}
	}


#define EXPERIENCE_HEADER \
	" Experience Costs:\r\n" \
	"  Attribute:        next level x 4\r\n" \
	"  Ability:          next level x 2      New Ability:     3\r\n" \
	"  Discipline:       next level x 5      New Discipline: 10\r\n" \
	"  Path (primary):   next level x 5\r\n" \
	"  Path (secondary): next level x 6\r\n" \
	""

#define BUY_STAT(name, ch, stat, cost_have, cost_new)		\
	else if (is_abbrev(argument, name)) {		\
		if ((cost_new) == -1 && !(stat))		\
			msg_to_char(ch, "You can't buy that as a new trait.\r\n");		\
		else if ((stat) >= att_max(ch))		\
			msg_to_char(ch, "That trait is already at maximum.\r\n");		\
		else if (MAX(1, (cost_new)) > GET_EXPERIENCE(ch) && !(stat))		\
			msg_to_char(ch, "You don't have enough experience to buy that trait.\r\n");		\
		else if ((cost_have) * ((stat) + 1) > GET_EXPERIENCE(ch) && (stat) > 0)		\
			msg_to_char(ch, "You don't have enough experience to buy a new level of that trait.\r\n");		\
		else {		\
			if ((stat) == 0)		\
				gain_experience(ch, -1 * (cost_new));		\
			else		\
				gain_experience(ch, -1 * ((stat) + 1) * (cost_have));		\
			(stat) += 1;		\
			msg_to_char(ch, OK);		\
			SAVE_CHAR(ch);		\
			}		\
		}


#define BUY_DISC(name, ch, discipline)		\
	else if (is_abbrev(argument, name))	{		\
		if (!IS_VAMPIRE(ch) && !IS_GHOUL(ch))		\
			msg_to_char(ch, "You can't buy disciplines!\r\n");		\
		else if (GET_REAL_DISC(ch, discipline) >= disc_max(ch))		\
			msg_to_char(ch, "That discipline is already at maximum.\r\n");		\
		else if (discipline == DISC_THAUMATURGY && GET_REAL_DISC(ch, discipline) >= 5)		\
			msg_to_char(ch, "That discipline is already at maximum.\r\n");		\
		else if (clan[GET_CLAN(ch)].disc[0] != discipline && clan[GET_CLAN(ch)].disc[1] != discipline && clan[GET_CLAN(ch)].disc[2] != discipline) \
			msg_to_char(ch, "You cannot buy a discipline from another clan.\r\n");		\
		else if (DISC_COST_NEW > GET_EXPERIENCE(ch) && !GET_REAL_DISC(ch, discipline))		\
			msg_to_char(ch, "You don't have enough experience to buy that discipline.\r\n");		\
		else if (DISC_COST_HAVE * (GET_REAL_DISC(ch, discipline) + 1) > GET_EXPERIENCE(ch) && GET_REAL_DISC(ch, discipline) > 0)		\
			msg_to_char(ch, "You don't have enough experience to buy a new level of that discipline.\r\n");		\
		else {		\
			if (GET_REAL_DISC(ch, discipline) == 0)		\
				gain_experience(ch, -1 * DISC_COST_NEW);		\
			else		\
				gain_experience(ch, -1 * (GET_REAL_DISC(ch, discipline) + 1) * DISC_COST_HAVE);		\
			GET_REAL_DISC(ch, discipline) += 1;		\
			if (discipline == DISC_THAUMATURGY)		\
				GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) = GET_THAUMATURGY(ch);	\
			msg_to_char(ch, OK);		\
			SAVE_CHAR(ch);		\
			}		\
		}


#define BUY_PATH(name, ch, path)		\
	else if (is_abbrev(argument, name))	{		\
		if (!IS_VAMPIRE(ch))		\
			msg_to_char(ch, "You can't buy paths!\r\n");		\
		else if (GET_REAL_PATH(ch, path) >= 5)		\
			msg_to_char(ch, "That path is already at maximum.\r\n");		\
		else if (path != GET_PRIMARY_PATH(ch) && GET_REAL_PATH(ch, path) + 1 >= GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) && GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) < 5)	\
			msg_to_char(ch, "You can't raise another path to or above the rating in your primary path.\r\n");	\
		else if (path == GET_PRIMARY_PATH(ch))	\
			msg_to_char(ch, "Just purchase more Thaumaturgy.\r\n");	\
		else if (GET_REAL_PATH(ch, path) >= GET_THAUMATURGY(ch))	\
			msg_to_char(ch, "You can't raise a path past your Thaumaturgy rating.\r\n");	\
		else if (PATH_COST_NEW > GET_EXPERIENCE(ch) && !GET_REAL_PATH(ch, path))		\
			msg_to_char(ch, "You don't have enough experience to buy that path.\r\n");		\
		else if (path != GET_PRIMARY_PATH(ch) && PATH_COST_SECONDARY * (GET_REAL_PATH(ch, path) + 1) > GET_EXPERIENCE(ch) && GET_REAL_PATH(ch, path) > 0)		\
			msg_to_char(ch, "You don't have enough experience to buy a new level of that path.\r\n");		\
		else {		\
			if (GET_REAL_PATH(ch, path) == 0)		\
				gain_experience(ch, -1 * PATH_COST_NEW);		\
			else		\
				gain_experience(ch, -1 * (GET_REAL_PATH(ch, path) + 1) * PATH_COST_SECONDARY);		\
			GET_REAL_PATH(ch, path) += 1;		\
			msg_to_char(ch, OK);		\
			SAVE_CHAR(ch);		\
			}		\
		}


ACMD(do_experience) {
	skip_spaces(&argument);

	if (IS_NPC(ch))
		msg_to_char(ch, "NPC's do not have experience.\r\n");
	else if (!*argument)
		msg_to_char(ch, "You have %d experience point%s to spend.\r\n%s", GET_EXPERIENCE(ch), GET_EXPERIENCE(ch) != 1 ? "s" : "", EXPERIENCE_HEADER);

	/* Attributes: Physical */
	BUY_STAT("strength", ch, ch->real_abils.strength, ATTRIBUTE_COST, -1)
	BUY_STAT("dexterity", ch, ch->real_abils.dexterity, ATTRIBUTE_COST, -1)
	BUY_STAT("stamina", ch, ch->real_abils.stamina, ATTRIBUTE_COST, -1)

	/* Attributes: Social */
	BUY_STAT("charisma", ch, ch->real_abils.charisma, ATTRIBUTE_COST, -1)
	BUY_STAT("manipulation", ch, ch->real_abils.manipulation, ATTRIBUTE_COST, -1)
	BUY_STAT("appearance", ch, ch->real_abils.appearance, ATTRIBUTE_COST, -1)

	/* Attributes: Mental */
	BUY_STAT("perception", ch, ch->real_abils.perception, ATTRIBUTE_COST, -1)
	BUY_STAT("intelligence", ch, ch->real_abils.intelligence, ATTRIBUTE_COST, -1)
	BUY_STAT("wits", ch, ch->real_abils.wits, ATTRIBUTE_COST, -1)

	/* Abilities: Talents */
	BUY_STAT("acting", ch, ch->player_specials->saved.talents[TALENT_ACTING], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("alertness", ch, ch->player_specials->saved.talents[TALENT_ALERTNESS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("athletics", ch, ch->player_specials->saved.talents[TALENT_ATHLETICS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("brawl", ch, ch->player_specials->saved.talents[TALENT_BRAWL], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("dodge", ch, ch->player_specials->saved.talents[TALENT_DODGE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("empathy", ch, ch->player_specials->saved.talents[TALENT_EMPATHY], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("intimidation", ch, ch->player_specials->saved.talents[TALENT_INTIMIDATION], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("larceny", ch, ch->player_specials->saved.talents[TALENT_LARCENY], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("leadership", ch, ch->player_specials->saved.talents[TALENT_LEADERSHIP], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("primal-urge", ch, ch->player_specials->saved.talents[TALENT_PRIMAL_URGE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("subterfuge", ch, ch->player_specials->saved.talents[TALENT_SUBTERFUGE], ABILITY_COST, ABILITY_COST_NEW)

	/* Abilities: Skills */
	BUY_STAT("animal ken", ch, ch->player_specials->saved.skills[SKILL_ANIMAL_KEN], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("archery", ch, ch->player_specials->saved.skills[SKILL_ARCHERY], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("crafts", ch, ch->player_specials->saved.skills[SKILL_CRAFTS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("etiquette", ch, ch->player_specials->saved.skills[SKILL_ETIQUETTE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("herbalism", ch, ch->player_specials->saved.skills[SKILL_HERBALISM], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("melee", ch, ch->player_specials->saved.skills[SKILL_MELEE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("music", ch, ch->player_specials->saved.skills[SKILL_MUSIC], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("performance", ch, ch->player_specials->saved.skills[SKILL_PERFORMANCE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("ride", ch, ch->player_specials->saved.skills[SKILL_RIDE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("stealth", ch, ch->player_specials->saved.skills[SKILL_STEALTH], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("survival", ch, ch->player_specials->saved.skills[SKILL_SURVIVAL], ABILITY_COST, ABILITY_COST_NEW)

	/* Abilities: Knowledges */
	BUY_STAT("academics", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_ACADEMICS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("enigmas", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_ENIGMAS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("hearth wisdom", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_HEARTH_WISDOM], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("investigation", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_INVESTIGATION], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("law", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_LAW], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("medicine", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_MEDICINE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("occult", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_OCCULT], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("politics", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_POLITICS], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("science", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_SCIENCE], ABILITY_COST, ABILITY_COST_NEW)
	BUY_STAT("seneschal", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_SENESCHAL], ABILITY_COST, ABILITY_COST_NEW)

	/* Disciplines */
	BUY_DISC("animalism", ch, DISC_ANIMALISM)
	BUY_DISC("auspex", ch, DISC_AUSPEX)
	BUY_DISC("celerity", ch, DISC_CELERITY)
	BUY_DISC("chimerstry", ch, DISC_CHIMERSTRY)
	BUY_DISC("dementation", ch, DISC_DEMENTATION)
	BUY_DISC("dominate", ch, DISC_DOMINATE)

	else if (affected_by_spell(ch, ATYPE_BESTOW_VIGOR) && is_abbrev(argument, "fortitude"))
		msg_to_char(ch, "You can't purchase Fortitude right now.\r\n");

	BUY_DISC("fortitude", ch, DISC_FORTITUDE)
	BUY_DISC("mortis", ch, DISC_MORTIS)
	BUY_DISC("obfuscate", ch, DISC_OBFUSCATE)
	BUY_DISC("obtenebration", ch, DISC_OBTENEBRATION)
	BUY_DISC("potence", ch, DISC_POTENCE)
	BUY_DISC("presence", ch, DISC_PRESENCE)
	BUY_DISC("protean", ch, DISC_PROTEAN)
	BUY_DISC("quietus", ch, DISC_QUIETUS)
	BUY_DISC("serpentis", ch, DISC_SERPENTIS)
	BUY_DISC("thaumaturgy", ch, DISC_THAUMATURGY)
	BUY_DISC("vicissitude", ch, DISC_VICISSITUDE)

	BUY_PATH("rego vitae", ch, PATH_REGO_VITAE)
	BUY_PATH("creo ignem", ch, PATH_CREO_IGNEM)
	BUY_PATH("rego motus", ch, PATH_REGO_MOTUS)
	BUY_PATH("rego tempestas", ch, PATH_REGO_TEMPESTAS)
	BUY_PATH("rego aquam", ch, PATH_REGO_AQUAM)
	BUY_PATH("rego elementum", ch, PATH_REGO_ELEMENTUM)
	BUY_PATH("way of levinbolt", ch, PATH_WAY_OF_LEVINBOLT)
	BUY_PATH("path of warding", ch, PATH_PATH_OF_WARDING)

	else
		msg_to_char(ch, "Invalid choice.\r\n");
	}