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.comm.c                                     EmpireMUD AD 1.0 *
*  Usage: Player-level communication 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.               *
************************************************************************ */

#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 "skills.h"

/* externs */
extern const char *langs[];


#define MAX_RECENT_CHANNELS		20		/* Number of pub_comm uses to remember */


/* * Structure for recording channel usage
 *  I know that this could record char_data, but if that character quits
 *  it would be lost.  invis_lev is recorded so we don't accidently
 *  undermine invisibility.  channel could be stored as a number, but
 *  we'd probably use more memory having a constant string of channel
 *  names, and that would be one more thing to change when adding a new
 *  channel.  For now, I think we're best off like this.  -PC
 */
struct recent_channel_data {
	char *name;						/* PERS() of person using channel	*/
	int min_lev;
	int invis_lev;					/* char's invis level, Someone		*/
	bool mort_invis;				/* mortal invis spell				*/
	char *channel;					/* name of channel					*/
	char *message;

	struct recent_channel_data *next;
	};

struct recent_channel_data *recent_channels;


/* Adds a channel usage to the recent_channels array */
void add_to_recent_channels(Creature ch, char *channel, char *message, bool normal, int level) {
	int count;
	struct recent_channel_data *chan, *rcd;

	/* Find the total amount and the last entry */
	for (count = 1, rcd = recent_channels; rcd && rcd->next; rcd = rcd->next, count++);
	if (count >= MAX_RECENT_CHANNELS) {
		chan = recent_channels;
		if (chan->name)
			free(chan->name);
		if (chan->channel)
			free(chan->channel);
		if (chan->message)
			free(chan->message);
		recent_channels = recent_channels->next;
		free(chan);
		}

	/* rcd is now the last entry */
	CREATE(chan, struct recent_channel_data, 1);

	chan->name = str_dup(PERS(ch, ch, normal));
	chan->min_lev = level;
	chan->invis_lev = GET_INVIS_LEV(ch);
	chan->mort_invis = AFF_FLAGGED(ch, AFF_INVISIBLE);
	chan->channel = str_dup(channel);
	chan->message = str_dup(message);

	if (rcd)
		rcd->next = chan;
	else
		recent_channels = chan;
	chan->next = NULL;
	}


/* Display all recent channel uses to a person */
void output_recent_channels(Creature ch) {
	struct recent_channel_data *rcd;

	for (rcd = recent_channels; rcd; rcd = rcd->next)
		if (GET_LEVEL(ch) >= rcd->min_lev)
			msg_to_char(ch, "(%s&0) %s&0: %s&0\r\n", rcd->channel, (GET_LEVEL(ch) >= rcd->invis_lev && (!rcd->mort_invis || PRF_FLAGGED(ch, PRF_HOLYLIGHT))) ? rcd->name : "Someone", rcd->message);
	}


ACMD(do_history) {
	msg_to_char(ch, "Last %d channels:\r\n", MAX_RECENT_CHANNELS);
	output_recent_channels(ch);
	}


ACMD(do_say) {
	Creature c;
	skip_spaces(&argument);

	if (!*argument)
		msg_to_char(ch, "Yes, but WHAT do you want to say?\r\n");
	else if (subcmd != SCMD_OOCSAY && ROOM_AFF_FLAGGED(ch->in_room, ROOM_AFF_SILENT))
		msg_to_char(ch, "You speak, but no words come out!\r\n");
	else {
		if (subcmd == SCMD_OOCSAY)
			strcpy(buf1, " out of character,");
		else
			sprintf(buf1, " in %s,", langs[(int) GET_SPEAKING(ch)]);
				//&6&bYou say, in english, &4'&0&6test&4&b'&0
		sprintf(buf, "&6&b$n says,%s &4'&0&6%s&4&b'&0", buf1, argument);

		for (c = world[ch->in_room].people; c; c = c->next_in_room) {
			if (REAL_NPC(c) || ch == c)
				continue;
			if (subcmd == SCMD_OOCSAY || IS_IMMORTAL(ch) || IS_GOD(ch) || IS_IMMORTAL(c) || IS_GOD(c) || IS_NPC(ch) || IS_NPC(ch) || IS_SET(GET_LANGUAGES(c), LANGUAGE_BIT(GET_SPEAKING(ch))))
				act(buf, FALSE, ch, 0, c, TO_VICT);
			else
				act("$n says something you don't understand.", FALSE, ch, 0, c, TO_VICT);
			}

		if (!REAL_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT))
			msg_to_char(ch, OK);
		else {
			delete_doubledollar(argument);
			msg_to_char(ch, "&6&bYou say,%s &4'&0&6%s&4&b'&0\r\n", buf1, argument);
			}
		}
	}


ACMD(do_psay) {
	Creature k;
	struct follow_type *f;

	skip_spaces(&argument);

	if (!AFF_FLAGGED(ch, AFF_PARTY)) {
		msg_to_char(ch, "But you are not the member of a party!\r\n");
		return;
		}
	if (!*argument)
		msg_to_char(ch, "Yes, but WHAT do you want to party-talk?\r\n");
	else {
		if (ch->master)
			k = ch->master;
		else
			k = ch;

		sprintf(buf, "$n tells the party, '%s'", argument);

		if (AFF_FLAGGED(k, AFF_PARTY) && (k != ch))
			act(buf, FALSE, ch, 0, k, TO_VICT | TO_SLEEP);
		for (f = k->followers; f; f = f->next)
			if (AFF_FLAGGED(f->follower, AFF_PARTY) && (f->follower != ch))
				if (!PRF_FLAGGED(f->follower, PRF_RP) && !IS_IMMORTAL(f->follower))
					act(buf, FALSE, ch, 0, f->follower, TO_VICT | TO_SLEEP);

		if (PRF_FLAGGED(ch, PRF_NOREPEAT))
			msg_to_char(ch, OK);
		else
			msg_to_char(ch, "You tell the party, '%s'\r\n", argument);
		}
	}


/* Send a tell to a person, sanity checks are done below */
void perform_tell(Creature ch, Creature vict, char *arg) {
	char **msg, msgbuf[MAX_STRING_LENGTH];

	sprintf(buf, "$o tells you, '%s'&0", arg);
	msg_to_char(vict, "&1&b");
	act(buf, FALSE, ch, 0, vict, TO_VICT | TO_SLEEP | TO_NODARK);

	if (!REAL_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT))
		msg_to_char(ch, OK);
	else {
		sprintf(buf, "&1&bYou tell $O, '%s'&0", arg);
		act(buf, FALSE, ch, 0, vict, TO_CHAR | TO_SLEEP | TO_NODARK);
		}

	if (IS_AFK(vict)) {
		act("$E is AFK and may not receive your message.", FALSE, ch, 0, vict, TO_CHAR);
		if (AFK_MSG(vict))
			msg_to_char(ch, "Message: %s\r\n", AFK_MSG(vict));
		Global_ignore_dark = TRUE;
		sprintf(msgbuf, "%s: %s\r\n", PERS(ch, vict, 1), arg);
		Global_ignore_dark = FALSE;
		if (AFK_MSGS(vict))
			sprintf(buf, "%s%s", AFK_MSGS(vict), msgbuf);
		else
			sprintf(buf, "%s", msgbuf);
		msg = &(AFK_MSGS(vict));
		if (*msg)
			free(*msg);
		if (!*buf)
			*msg = NULL;
		else
			*msg = str_dup(buf);
		}

	if (!REAL_NPC(vict) && !REAL_NPC(ch))
		GET_LAST_TELL(vict) = GET_IDNUM(ch);
	}

/* Verifies the tellability of a person */
int is_tell_ok(Creature ch, Creature vict) {
	if (ch == vict)
		msg_to_char(ch, "You try to tell yourself something.\r\n");
	else if (!REAL_NPC(ch) && PRF_FLAGGED(ch, PRF_NOTELL))
		msg_to_char(ch, "You can't tell other people while you have notell on.\r\n");
	else if (!REAL_NPC(vict) && !vict->desc)        /* linkless */
		act("$E's linkless at the moment.", FALSE, ch, 0, vict, TO_CHAR | TO_SLEEP);
	else if (PLR_FLAGGED(vict, PLR_WRITING))
		act("$E's writing a message right now; try again later.", FALSE, ch, 0, vict, TO_CHAR | TO_SLEEP);
	else if ((!REAL_NPC(vict) && PRF_FLAGGED(vict, PRF_NOTELL)))
		act("$E can't hear you.", FALSE, ch, 0, vict, TO_CHAR | TO_SLEEP);
	else if (!REAL_NPC(vict) && PRF_FLAGGED(vict, PRF_RP) && !IS_IMMORTAL(ch) && !IS_IMMORTAL(vict))
		msg_to_char(ch, "You can't use tell with people who are role-playing.\r\n");
	else
		return (TRUE);

	return (FALSE);
	}


ACMD(do_tell) {
	Creature vict = NULL;

	if (REAL_NPC(ch))
		return;

	half_chop(argument, buf, buf2);

	if (!*buf || !*buf2)
		msg_to_char(ch, "Who do you wish to tell what??\r\n");
	else if (!(vict = get_player_vis(ch, buf, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (is_tell_ok(ch, vict))
		perform_tell(ch, vict, buf2);
	}


ACMD(do_reply) {
	Creature tch = character_list;

	if (REAL_NPC(ch))
		return;

	skip_spaces(&argument);

	if (GET_LAST_TELL(ch) == NOBODY)
		msg_to_char(ch, "You have no-one to reply to!\r\n");
	else if (!*argument)
		msg_to_char(ch, "What is your reply?\r\n");
	else {
		/*
		 * Make sure the person you're replying to is still playing by searching
		 * for them.  Note, now last tell is stored as player IDnum instead of
		 * a pointer, which is much better because it's safer, plus will still
		 * work if someone logs out and back in again.
		 */

		/*
		 * XXX: A descriptor list based search would be faster although
		 *      we could not find link dead people.  Not that they can
		 *      hear tells anyway. :) -gg 2/24/98
		 */
		while (tch != NULL && (REAL_NPC(tch) || GET_IDNUM(tch) != GET_LAST_TELL(ch)))
			tch = tch->next;

		if (tch == NULL)
			msg_to_char(ch, "They are no longer playing.\r\n");
		else if (is_tell_ok(ch, tch))
			perform_tell(ch, tch, argument);
		}
	}


ACMD(do_spec_comm) {
	Creature vict;
	const char *action_sing, *action_plur, *action_others;

	switch (subcmd) {
		case SCMD_WHISPER:
			action_sing = "whisper to";
			action_plur = "whispers to";
			action_others = "$n whispers something to $N.";
			break;
		case SCMD_ASK:
			action_sing = "ask";
			action_plur = "asks";
			action_others = "$n asks $N a question.";
			break;
		default:
			action_sing = "oops";
			action_plur = "oopses";
			action_others = "$n is tongue-tied trying to speak with $N.";
			break;
			}

	half_chop(argument, buf, buf2);

	if (!*buf || !*buf2) {
		sprintf(buf, "Whom do you want to %s.. and what??\r\n", action_sing);
		msg_to_char(ch, buf);
		}
	else if (ROOM_AFF_FLAGGED(ch->in_room, ROOM_AFF_SILENT))
		msg_to_char(ch, "You speak, but no words come out!\r\n");
	else if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM)))
		msg_to_char(ch, NOPERSON);
	else if (vict == ch)
		msg_to_char(ch, "You can't get your mouth close enough to your ear...\r\n");
	else {
		sprintf(buf, "$n %s you, '%s'", action_plur, buf2);
		act(buf, FALSE, ch, 0, vict, TO_VICT);
		if (PRF_FLAGGED(ch, PRF_NOREPEAT))
			msg_to_char(ch, OK);
		else
			msg_to_char(ch, "You %s %s, '%s'\r\n", action_sing, PERS(vict, ch, 0), buf2);
		act(action_others, FALSE, ch, 0, vict, TO_NOTVICT);
		}
	}


ACMD(do_page) {
	Descr d;
	Creature vict;

	half_chop(argument, arg, buf2);

	if (REAL_NPC(ch))
		msg_to_char(ch, "Monsters can't page.. go away.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Whom do you wish to page?\r\n");
	else {
		sprintf(buf, "\007*$n* %s", buf2);
		if (!str_cmp(arg, "all")) {
			if (GET_LEVEL(ch) >= LVL_IMPL) {
				for (d = descriptor_list; d; d = d->next)
					if (STATE(d) == CON_PLAYING && d->character)
						act(buf, FALSE, ch, 0, d->character, TO_VICT);
				}
			else
				msg_to_char(ch, "You will never be godly enough to do that!\r\n");
			return;
			}
		if ((vict = get_char_vis(ch, arg, FIND_CHAR_WORLD)) != NULL) {
			act(buf, FALSE, ch, 0, vict, TO_VICT);
			if (PRF_FLAGGED(ch, PRF_NOREPEAT))
				msg_to_char(ch, OK);
			else
				act(buf, FALSE, ch, 0, vict, TO_CHAR);
			}
		else
			msg_to_char(ch, "There is no such person in the game!\r\n");
		}
	}


/***********************************************************************
 * generalized communication func, originally by Fred C. Merkel (Torg) *
 * This function has been rewritten (several times) by Paul Clarke     *
 * (Gideon) for TBG use.  The basis remains the same, but the output   *
 * and handling have been customized. -Paul, 11/19/98                  *
 **********************************************************************/

/* Basic distance */
#define distance(x, y, a, b)		((x - a) * (x - a) + (y - b) * (y - b))

ACMD(do_pub_comm) {
	Descr i;
	char level_string[10], invis_string[10];
	bool emote = 0;
	int level = 0, min_lev = 0;

	/* Array of flags which must _not_ be set in order for comm to be heard */
	static int channels[] = {
		PRF_DEAF,
		PRF_NOGOSS,
		PRF_NOOOC,
		PRF_NOGODNET,
		PRF_NOWIZ
		};

	/*
	 * com_msgs: [0] Message if you can't perform the action because of mute.
	 *           [1] name of the action.
	 *           [2] message if you're not on the channel.
	 *           [3] a color string.
	 *           [4] channel type:
	 *               "0": $n shouts, '...' (limited by distance)
	 *               "1": $n shouts, '...'
	 *               "2": [SHOUT $n]: ...
	 */
	static char *com_msgs[][5] = {
		{ "You cannot shout!!\r\n",
			"shout",
			"You aren't even on the channel!\r\n",
			"&3",
			"0" },

		{ "You can't gossip!\r\n",
			"gossip",
			"You aren't even on the channel!\r\n",
			"&3",
			"1" },

		{ "You can't use the out-of-character channel!\r\n",
			"OOC",
			"You aren't even on the channel!\r\n",
			"&3",
			"2" },

		{ "You can't use the god channel!\r\n",
			"GOD",
			"You aren't even on the channel!\r\n",
			"&1",
			"2" },

		{ "You can't use the immortal channel!\r\n",
			"IMMORTAL",
			"You aren't even on the channel!\r\n",
			"&6",
			"3" }

		};

	/* to keep pets, etc from being ordered to shout */
	if (AFF_FLAGGED(ch, AFF_CHARM) && !ch->desc)
		return;

	if (PLR_FLAGGED(ch, PLR_MUTED)) {
		msg_to_char(ch, com_msgs[subcmd][0]);
		return;
		}

	if (atoi(com_msgs[subcmd][4]) == 2 && REAL_NPC(ch))
		return;

	if (atoi(com_msgs[subcmd][4]) == 0 && ROOM_AFF_FLAGGED(ch->in_room, ROOM_AFF_SILENT)) {
		msg_to_char(ch, "You try to, but no words come out!\r\n");
		return;
		}

	/* make sure the char is on the channel */
	if (channels[subcmd] && PRF_FLAGGED(ch, channels[subcmd])) {
		msg_to_char(ch, com_msgs[subcmd][2]);
		return;
		}
	/* skip leading spaces */
	skip_spaces(&argument);

	/* make sure that there is something there to say! */
	if (!*argument) {
		sprintf(buf, "Yes, %s, fine, %s we must, but WHAT???\r\n", com_msgs[subcmd][1], com_msgs[subcmd][1]);
		msg_to_char(ch, buf);
		return;
		}

	if (atoi(com_msgs[subcmd][4]) == 2 || atoi(com_msgs[subcmd][4]) == 3) {
		if (*argument == '*') {
			emote = TRUE;
			argument++;
			}
		if (*argument == '#') {
			one_argument(argument + 1, buf1);
			if (is_number(buf1)) {
				half_chop(argument+1, buf1, argument);
				level = atoi(buf1);
				if (level > GET_REAL_LEVEL(ch)) {
					msg_to_char(ch, "You can't speak above your own level.\r\n");
					return;
					}
				}
			}
		if (!emote)
			if (*argument == '*') {
				emote = TRUE;
				argument++;
				}

		skip_spaces(&argument);
		}

	/* override levels */
	switch (subcmd) {
		case SCMD_WIZNET:	min_lev = LVL_START_IMM;	break;
		case SCMD_GODNET:	min_lev = LVL_GOD;			break;
		}

	level = MAX(level, min_lev);

	if (level > min_lev)
		sprintf(level_string, " <%d>", level);
	else
		level_string[0] = '\0';

	if (GET_INVIS_LEV(ch) > 0)
		sprintf(invis_string, " (i%d)", GET_INVIS_LEV(ch));
	else
		*invis_string = '\0';

	/* Add to the public message archive, as above */
	sprintf(buf, "%s%s", com_msgs[subcmd][3], com_msgs[subcmd][1]);
	if (atoi(com_msgs[subcmd][4]) != 0 && atoi(com_msgs[subcmd][4]) != 3)
		add_to_recent_channels(ch, buf, argument, (atoi(com_msgs[subcmd][4]) == 2), level);

	switch(atoi(com_msgs[subcmd][4])) {
		case 0:
		case 1:
			if (PRF_FLAGGED(ch, PRF_NOREPEAT))
				msg_to_char(ch, OK);
			else {
				sprintf(buf1, "%sYou %s, in %s, '%s'&0", com_msgs[subcmd][3], com_msgs[subcmd][1], langs[(int) GET_SPEAKING(ch)], argument);
				act(buf1, FALSE, ch, 0, 0, TO_CHAR | TO_SLEEP);
				}
			for (i = descriptor_list; i; i = i->next) {
				if (STATE(i) == CON_PLAYING && i != ch->desc && i->character && (!channels[subcmd] || !PRF_FLAGGED(i->character, channels[subcmd])) && !PLR_FLAGGED(i->character, PLR_WRITING)) {
					/* distance */
					if (atoi(com_msgs[subcmd][4]) == 0 && distance(X_COORD(ch->in_room), Y_COORD(ch->in_room), X_COORD(i->character->in_room), Y_COORD(i->character->in_room)) > distance(0, 0, 50, 0))
						continue;

					/* language: gossip-style */
					if (atoi(com_msgs[subcmd][4]) == 1 && !IS_IMMORTAL(ch) && !IS_GOD(ch) && !IS_IMMORTAL(i->character) && !IS_GOD(i->character) && !IS_NPC(ch) && !IS_NPC(i->character) && !IS_SET(GET_LANGUAGES(i->character), LANGUAGE_BIT(GET_SPEAKING(ch))))
						continue;

					if (atoi(com_msgs[subcmd][4]) == 0 && ROOM_AFF_FLAGGED(i->character->in_room, ROOM_AFF_SILENT) && GET_QUIETUS(i->character) < 1)
						continue;

					sprintf(buf, "$n %ss, in %s, '%s'&0", com_msgs[subcmd][1], langs[(int) GET_SPEAKING(ch)], argument);
					sprintf(buf1, "$n %ss something you don't understand.&0", com_msgs[subcmd][1]);
					msg_to_char(i->character, "%s", com_msgs[subcmd][3]);

					if (!IS_IMMORTAL(ch) && !IS_GOD(ch) && !IS_IMMORTAL(i->character) && !IS_GOD(i->character) && !IS_NPC(ch) && !IS_NPC(i->character) && !IS_SET(GET_LANGUAGES(i->character), LANGUAGE_BIT(GET_SPEAKING(ch))))
						act(buf1, FALSE, ch, 0, i->character, TO_VICT | TO_SLEEP);
					else
						act(buf, FALSE, ch, 0, i->character, TO_VICT | TO_SLEEP);
					}
				}
			break;
		case 2:
		case 3:
		default:
			/* No invis checks here for name, this is to self */
			if (PRF_FLAGGED(ch, PRF_NOREPEAT))
				msg_to_char(ch, OK);
			else {
				if (emote)
					sprintf(buf, "&0[%s%s&0%s%s] $o %s&0", com_msgs[subcmd][3], com_msgs[subcmd][1], invis_string, level_string, argument);
				else
					sprintf(buf, "&0[%s%s&0 $o%s%s]: %s&0", com_msgs[subcmd][3], com_msgs[subcmd][1], invis_string, level_string, argument);
				act(buf, FALSE, ch, 0, 0, TO_CHAR | TO_SLEEP);
				}

			for (i = descriptor_list; i; i = i->next) {
				if (STATE(i) != CON_PLAYING || i == ch->desc || !i->character || PLR_FLAGGED(i->character, PLR_WRITING))
					continue;
				if (channels[subcmd] && PRF_FLAGGED(i->character, channels[subcmd]))
					continue;
				if (GET_REAL_LEVEL(i->character) < level)
					continue;
				if (atoi(com_msgs[subcmd][4]) == 2 && PRF_FLAGGED(i->character, PRF_RP) && !IS_IMMORTAL(i->character))
					continue;

				if (emote)
					sprintf(buf, "&0[%s%s&0%s%s] %s %s&0", com_msgs[subcmd][3], com_msgs[subcmd][1], CAN_SEE_NO_DARK(i->character, ch) ? invis_string : "", level_string, !CAN_SEE_NO_DARK(i->character, ch) ? "Someone" : "$o", argument);
				else
					sprintf(buf, "&0[%s%s&0 %s%s%s]: %s&0", com_msgs[subcmd][3], com_msgs[subcmd][1], !CAN_SEE_NO_DARK(i->character, ch) ? "Someone" : "$o", CAN_SEE_NO_DARK(i->character, ch) ? invis_string : "", level_string, argument);

				act(buf, FALSE, ch, 0, i->character, TO_VICT | TO_SLEEP | TO_NODARK);
				}
			break;
		}
	}


/* Output text on a virtual channel */
void perform_vcsay(Creature ch, long vc, char *vc_name, char *string) {
	Descr d;

	if (ch) {
		sprintf(buf, "[%s %s]: %s\r\n", vc_name, PERS(ch, ch, 1), string);
		sprintf(buf1, "[%s Someone]: %s\r\n", vc_name, string);
		}
	else
		sprintf(buf1, "[%s SYSTEM]: %s\r\n", vc_name, string);

	for (d = descriptor_list; d; d = d->next) {
		if (!d->character || STATE(d) != CON_PLAYING)
			continue;
		if (GET_VC(d->character) != vc)
			continue;
		if (PRF_FLAGGED(d->character, PRF_RP) && !IS_IMMORTAL(d->character))
			continue;

		if (ch && CAN_SEE(d->character, ch))
			msg_to_char(d->character, buf);
		else
			msg_to_char(d->character, buf1);
		}
	}


ACMD(do_vcsay) {
	skip_spaces(&argument);

	if (IS_NPC(ch))
		msg_to_char(ch, "You're probably not really on a virtual channel..\r\n");
	else if (GET_VC(ch) < 1)
		msg_to_char(ch, "You're not on a virtual channel.\r\n");
	else if (PLR_FLAGGED(ch, PLR_MUTED))
		msg_to_char(ch, "You can't talk on a virtual channel while muted.\r\n");
	else if (!*argument)
		msg_to_char(ch, "Say what on the virtual channel?\r\n");
	else
		perform_vcsay(ch, GET_VC(ch), GET_VC_NAME(ch), argument);
	}


ACMD(do_vc) {
	int i;
	Descr d;
	Creature c;
	char arg1[MAX_STRING_LENGTH];

	char *commands[] = {
		"open", "close", "invite", "join", "moderater", "rename", "kick",
		"who",
		"\n"
		};

	two_arguments(argument, arg, arg1);

	if (IS_NPC(ch))
		return;

	/* Find the command now */
	for (i = 0; str_cmp(commands[i], "\n") && !is_abbrev(arg, commands[i]); i++);

	if (!*arg || !str_cmp(commands[i], "\n")) {
		msg_to_char(ch, "Available virtual channel commands:\r\n");
		for (i = 0; str_cmp(commands[i], "\n"); i++)
			msg_to_char(ch, " %s\r\n", commands[i]);
		return;
		}

	if (PLR_FLAGGED(ch, PLR_MUTED)) {
		msg_to_char(ch, "You can't use a virtual channel while muted.\r\n");
		return;
		}

	switch (i) {
		case 0:		/* open */
			if (GET_VC(ch) > 0)
				msg_to_char(ch, "You're already on a virtual channel!\r\n");
			else if (!*arg1)
				msg_to_char(ch, "What would you like to name the virtual channel?\r\n");
			else {
				GET_VC(ch) = GET_IDNUM(ch);
				if (GET_VC_NAME(ch))
					free(GET_VC_NAME(ch));
				GET_VC_NAME(ch) = str_dup(arg1);
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), "Virtual channel open.");
				}
			return;
		case 1:		/* close */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not on a virtual channel.\r\n");
			else if (GET_VC(ch) != GET_IDNUM(ch)) {
				sprintf(buf2, "%s has left the virtual channel.", PERS(ch, ch, 1));
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), buf2);
				GET_VC(ch) = 0;
				}
			else {
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), "Virtual channel closed.");
				for (d = descriptor_list; d; d = d->next)
					if (d->character && d->character != ch && (GET_VC(d->character) == GET_VC(ch) || GET_VC(d->character) == GET_VC(ch) * -1))
						GET_VC(d->character) = 0;
				GET_VC(ch) = 0;
				}
			return;
		case 2:		/* invite */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not even on a virtual channel!\r\n");
			else if (GET_VC(ch) != GET_IDNUM(ch))
				msg_to_char(ch, "You're not the moderator of the virtual channel!\r\n");
			else if (!*arg1)
				msg_to_char(ch, "Invite whom to the virtual channel?\r\n");
			else if (!(c = get_char_vis(ch, arg1, FIND_CHAR_WORLD)))
				msg_to_char(ch, NOPERSON);
			else if (c == ch)
				msg_to_char(ch, "You're already on the channel!\r\n");
			else if (IS_NPC(c))
				msg_to_char(ch, "You can't add NPC's to the channel!\r\n");
			else if (GET_VC(c) > 0)
				msg_to_char(ch, "You can't invite someone who's already on a virtual channel.\r\n");
			else {
				GET_VC(c) = -1 * GET_VC(ch);
				if (GET_VC_NAME(c))
					free(GET_VC_NAME(c));
				GET_VC_NAME(c) = str_dup(GET_VC_NAME(ch));
				msg_to_char(c, "You have been invited to join virtual channel '%s'.\r\n", GET_VC_NAME(c));
				msg_to_char(ch, OK);
				}
			return;
		case 3:		/* join */
			if (GET_VC(ch) > 0)
				msg_to_char(ch, "You're already on a virtual channel!\r\n");
			else if (GET_VC(ch) == 0)
				msg_to_char(ch, "You haven't been invited to join any virtual channel!\r\n");
			else {
				GET_VC(ch) = -1 * GET_VC(ch);
				sprintf(buf2, "%s has joined this virtual channel.", PERS(ch, ch, 1));
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), buf2);
				}
			return;
		case 4:		/* moderator */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not even on a virtual channel!\r\n");
			else if (GET_VC(ch) != GET_IDNUM(ch))
				msg_to_char(ch, "You're not even the moderator of the channel!\r\n");
			else if (!*arg1)
				msg_to_char(ch, "Make whom the moderator of the channel?\r\n");
			else if (!(c = get_char_vis(ch, arg1, FIND_CHAR_WORLD)))
				msg_to_char(ch, NOPERSON);
			else if (IS_NPC(c) || GET_VC(c) != GET_VC(ch))
				msg_to_char(ch, "That person isn't even on the channel!\r\n");
			else if (c == ch)
				msg_to_char(ch, "You're already the moderator!\r\n");
			else {
				for (d = descriptor_list; d; d = d->next)
					if (d->character && d->character != ch && (GET_VC(d->character) == GET_VC(ch) || GET_VC(d->character) == GET_VC(ch) * -1))
						GET_VC(d->character) = (GET_VC(d->character) > 0 ? GET_IDNUM(c) : -1 * GET_IDNUM(c));
				GET_VC(ch) = GET_IDNUM(c);
				sprintf(buf2, "%s is now the moderator of this channel.", PERS(c, c, 1));
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), buf2);
				}
			return;
		case 5:		/* rename */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not even on a virtual channel!\r\n");
			else if (GET_VC(ch) != GET_IDNUM(ch))
				msg_to_char(ch, "You're not even the moderator of the channel!\r\n");
			else if (!*arg1)
				msg_to_char(ch, "Rename the virtual channel to what?\r\n");
			else {
				for (d = descriptor_list; d; d = d->next)
					if (d->character && (GET_VC(d->character) == GET_VC(ch) || GET_VC(d->character) == GET_VC(ch) * -1)) {
						if (GET_VC_NAME(d->character))
							free(GET_VC_NAME(d->character));
						GET_VC_NAME(d->character) = str_dup(arg1);
						}
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), "The virtual channel has been renamed.");
				}
			return;
		case 6:		/* kick */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not even on a virtual channel!\r\n");
			else if (GET_VC(ch) != GET_IDNUM(ch))
				msg_to_char(ch, "You're not even the moderator of the channel!\r\n");
			else if (!*arg1)
				msg_to_char(ch, "Kick who off the channel?\r\n");
			else if (!(c = get_char_vis(ch, arg1, FIND_CHAR_WORLD)))
				msg_to_char(ch, NOPERSON);
			else if (IS_NPC(c) || GET_VC(c) != GET_VC(ch))
				msg_to_char(ch, "That person isn't even on the channel!\r\n");
			else if (c == ch)
				msg_to_char(ch, "Use 'vc close' for that!\r\n");
			else {
				msg_to_char(c, "You have been kicked off the virtual channel.\r\n");
				GET_VC(c) = 0;
				sprintf(buf2, "%s has been kicked off the channel.", PERS(c, c, 1));
				perform_vcsay(NULL, GET_VC(ch), GET_VC_NAME(ch), buf2);
				}
			return;
		case 7:		/* who */
			if (GET_VC(ch) < 1)
				msg_to_char(ch, "You're not even on a virtual channel!\r\n");
			else {
				msg_to_char(ch, "People on virtual channel %s:\r\n", GET_VC_NAME(ch));
				for (d = descriptor_list; d; d = d->next)
					if (d->character && (GET_VC(d->character) == GET_VC(ch) || GET_VC(d->character) == GET_VC(ch) * -1))
						msg_to_char(ch, " %s%s\r\n", CAN_SEE(ch, d->character) ? PERS(d->character, ch, 1) : "Someone", GET_VC(d->character) < 0 ? " (invited)" : "");
				}
			return;
		default:
			msg_to_char(ch, "Invalid virtual channel ability.\r\n");
			return;
		}
	}


ACMD(do_speak) {
	extern const char *langs[];

	int i;
	bool c = FALSE;

	one_argument(argument, arg);

	if (IS_NPC(ch))
		return;

	if (!*arg) {
		msg_to_char(ch, "You are currently speaking %s.\r\n", langs[(int) GET_SPEAKING(ch)]);
		msg_to_char(ch, "You know how to speak");
		for (i = 0; i < NUM_LANGS; i++)
			if (IS_SET(GET_LANGUAGES(ch), LANGUAGE_BIT(i))) {
				msg_to_char(ch, "%s %s", c ? "," : "", langs[i]);
				c = TRUE;
				}
		msg_to_char(ch, ".\r\n");
		}
	else if ((i = search_block(arg, langs, FALSE)) < 0)
		msg_to_char(ch, "Invalid language.\r\n");
	else if (GET_SPEAKING(ch) == i)
		msg_to_char(ch, "You're already speaking that language.\r\n");
	else if (!IS_SET(GET_LANGUAGES(ch), LANGUAGE_BIT(i)))
		msg_to_char(ch, "You don't know how to speak that language.\r\n");
	else {
		SET_SPEAKING(ch) = i;
		msg_to_char(ch, "You will now speak %s.\r\n", langs[i]);
		}
	}