1stMUD/corefiles/
1stMUD/gods/
1stMUD/notes/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/

#include <sys/types.h>
#if !defined(WIN32)
#include <unistd.h>
#include <sys/time.h>
#else
#include "../win32/winstuff.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "merc.h"
#include "interp.h"
#include "recycle.h"
#include "tables.h"
#include "lookup.h"
#include "olc.h"

/* RT code to delete yourself */

CH_CMD(do_delet)
{
	chprintln(ch, "You must type the full command to delete yourself.");
}

CH_CMD(do_delete)
{
	char strsave[MAX_INPUT_LENGTH];
	int hash;
	ROOM_INDEX_DATA *pRoom;
	void update_webpasses(CHAR_DATA * ch, bool pDelete);

	if (IS_NPC(ch))
		return;

	if (ch->pcdata->confirm_delete)
	{
		if (argument[0] != '\0')
		{
			chprintln(ch, "Delete status removed.");
			ch->pcdata->confirm_delete = FALSE;
			return;
		}
		else
		{
			delete_home(ch);
			update_statlist(ch, TRUE);
			update_webpasses(ch, TRUE);
			if (is_clan(ch))
			{
				update_members(ch, TRUE);
			}
			for (hash = 0; hash < MAX_KEY_HASH; hash++)
			{
				for (pRoom = room_index_hash[hash]; pRoom != NULL;
					 pRoom = pRoom->next)
				{
					if (!IS_NULLSTR(pRoom->owner)
						&& !str_cmp(pRoom->owner, ch->name))
					{
						replace_string(pRoom->owner, "");
					}
				}
			}
			sprintf(strsave, "%s%s", PLAYER_DIR, capitalize(ch->name));
			wiznet("$N turns $Mself into line noise.", ch, NULL, 0, 0, 0);
			stop_fighting(ch, TRUE);
			do_function(ch, &do_quit, "");
			unlink(strsave);
			return;
		}
	}

	if (argument[0] != '\0')
	{
		chprintln(ch, "Just type delete. No argument.");
		return;
	}

	chprintln(ch, "Type delete again to confirm this command.");
	chprintln(ch, "WARNING: this command is irreversible.");
	chprintln(ch, "Typing delete with an argument will undo delete status.");
	ch->pcdata->confirm_delete = TRUE;
	wiznet("$N is contemplating deletion.", ch, NULL, 0, 0, get_trust(ch));
}

void update_last_data(CHAR_DATA * sender, CHAR_DATA * viewer, int last,
					  const char *chan, const char *str, int type)
{
	int i;
	char time[MIL];
	char buf[MSL];

	if (IS_NPC(viewer) || IS_NULLSTR(str) || IS_NULLSTR(chan) || last < 0
		|| last >= LAST_MAX)
		return;

	for (i = LAST_PAGE_LENGTH - 1; i > 0; i--)
	{
		replace_string(viewer->pcdata->history[last][i],
					   viewer->pcdata->history[last][i - 1]);
	}

	strftime(time, 100, "%r", localtime(&current_time));
	switch (type)
	{
	case CHANNEL_NORMAL:
		sprintf(buf, "[%s] %s{x %s says '%s'{x", time, chan, PERS(sender,
																  viewer), str);
		replace_string(viewer->pcdata->history[last][0], buf);
		break;
	case CHANNEL_SOCIAL:
		sprintf(buf, "[%s] %s{x %s{x", time, chan, str);
		replace_string(viewer->pcdata->history[last][0], buf);
		break;
	case CHANNEL_EMOTE:
		sprintf(buf, "[%s] %s{x %s %s{x",
				time, chan, PERS(sender, viewer), str);
		replace_string(viewer->pcdata->history[last][0], buf);
		break;
	default:
		bugf("bad channel type [%d]", type);
		break;
	}
}

void view_last_data(CHAR_DATA * ch, int last)
{
	int i;
	bool found = FALSE;

	if (last < 0 || last >= LAST_MAX)
		return;

	for (i = LAST_PAGE_LENGTH - 1; i >= 0; i--)
	{
		if (!IS_NULLSTR(ch->pcdata->history[last][i]))
		{
			found = TRUE;
			chprintln(ch, ch->pcdata->history[last][i]);
		}
	}
	if (!found)
		chprintln(ch, "None.");
}

bool display_channel(CHAR_DATA * ch, CHAR_DATA * victim,
					 enum special_flags spec_flag)
{
	if (!ch || !victim)
		return FALSE;

	if (IS_SET(victim->comm, COMM_QUIET))
		return FALSE;

	switch (spec_flag)
	{
	case spec_clan_flag:
		if (!is_same_clan(ch, victim))
			return FALSE;
		break;
	case spec_imm_flag:
		if (!IS_IMMORTAL(victim))
			return FALSE;
		break;
	case spec_buddy_flag:
		if (victim != ch
			&& (check_buddy(ch, victim) == -1 || check_buddy(victim, ch) == -1))
			return FALSE;
		break;
	case spec_public_flag:
		return TRUE;
		break;
	}

	return TRUE;
}

void channel_social(CHAR_DATA * ch, CHAR_DATA * victim,
					flag_t bit, const char *string, const char *type,
					enum special_flags spec_flag, int last_type)
{
	DESCRIPTOR_DATA *d;

	for (d = descriptor_first; d; d = d->next)
	{
		CHAR_DATA *vch = CH(d);

		if (d->connected != CON_PLAYING)
			continue;

		if (vch && (vch != ch) && (vch != victim)
			&& display_channel(ch, vch, spec_flag)
			&& (!bit || !IS_SET(vch->comm, bit))
			&& !IS_SET(vch->comm, COMM_NOGOCIAL))
		{
			char buf[MSL];

			sprintf(buf, "%s %s", type, string);
			perform_act(buf, ch, NULL, victim, 0, vch);
			update_last_data(ch, vch, last_type, type,
							 perform_act_string(string, ch, NULL, victim,
												FALSE), CHANNEL_SOCIAL);
		}
	}
	update_last_data(ch, ch, last_type, type,
					 perform_act_string(string, ch, NULL, victim, FALSE),
					 CHANNEL_SOCIAL);
}

void public_ch(CHAR_DATA * ch, const char *argument,
			   const char *type, flag_t bitname, enum special_flags spec_flag,
			   int last_type)
{
	char command[MIL + 100];
	DESCRIPTOR_DATA *d;
	bool display_wholist = FALSE, fEmote = FALSE;
	char arg_left[MSL];

	if (IS_NULLSTR(argument))
	{
		if (!bitname)
			chprintln(ch, "What do you want to say?");
		else
		{
			if (IS_SET(ch->comm, (bitname)))
			{
				chprintlnf(ch, "%s channel is now ON.{x", type);
				REMOVE_BIT(ch->comm, (bitname));
			}
			else
			{
				chprintlnf(ch, "%s channel is now OFF.{x", type);
				SET_BIT(ch->comm, (bitname));
			}
		}
	}
	else
	{
		if (IS_SET(ch->comm, COMM_QUIET))
		{
			chprintln(ch, "You must turn off quiet mode first.");
			return;
		}
		if (IS_SET(ch->comm, COMM_NOCHANNELS))
		{
			chprintln(ch, "The gods have revoked your channel priviliges.");
			return;
		}
		if (bitname)
			REMOVE_BIT(ch->comm, (bitname));

		strcpy(arg_left, argument);

		argument = one_argument(argument, command);
		if (!str_cmp(command, "+"))
		{
			CHAR_DATA *victim;
			char buf[MIL + 200];
			SOCIAL_DATA *cmd;
			char argx[MIL];

			argument = one_argument(argument, command);
			if (IS_NULLSTR(command))
			{
				chprintln
					(ch,
					 "{W<Channel> + <social> is used for channel based socials.{x");
				return;
			}
			if (!(cmd = find_social(command)))
			{
				chprintln(ch, "{WWhat kind of social is that?!?!{x");
				return;
			}
			one_argument(argument, argx);
			victim = NULL;
			if (IS_NULLSTR(argx))
			{
				sprintf(buf, "%s %s", type, cmd->char_no_arg);
				act_new(buf, ch, NULL, NULL, TO_CHAR, POS_DEAD);
				channel_social(ch, NULL, bitname,
							   cmd->others_no_arg, type, spec_flag, last_type);
			}
			else if ((victim = get_char_world(ch, argx)) == NULL)
			{
				chprintln(ch, "They aren't here.");
				return;
			}
			else
			{
				if (!display_channel(ch, victim, spec_flag))
				{
					chprintln(ch, "They can't use that channel.");
					return;
				}
				if (victim == ch)
				{
					sprintf(buf, "%s %s", type, cmd->char_auto);
					act_new(buf, ch, NULL, NULL, TO_CHAR, POS_DEAD);
					channel_social(ch, victim, bitname,
								   cmd->others_auto, type,
								   spec_flag, last_type);
				}
				else
				{
					sprintf(buf, "%s %s", type, cmd->char_found);
					act_new(buf, ch, NULL, victim, TO_CHAR, POS_DEAD);
					if ((!bitname || !IS_SET(victim->comm, bitname))
						&& !IS_SET(victim->comm, COMM_NOGOCIAL)
						&& display_channel(ch, victim, spec_flag))
					{
						sprintf(buf, "%s %s", type, cmd->vict_found);
						act_new(buf, ch, NULL, victim, TO_VICT, POS_DEAD);
					}
					channel_social(ch, victim, bitname,
								   cmd->others_found, type,
								   spec_flag, last_type);
				}
			}
			return;
		}
		else if (!str_cmp(command, "!"))
		{
			fEmote = TRUE;

			chprintlnf(ch, "%s %s %s{x", type,
					   IS_NPC(ch) ? ch->short_descr : ch->name, argument);
			update_last_data(ch, ch, last_type, type, argument, CHANNEL_EMOTE);
		}
		else if (!str_cmp(command, "wholist"))
		{
			display_wholist = TRUE;
			chprintlnf(ch, "{WPlayers on %s{x", type);
			chprintln(ch, "{C-------------------{x");
		}
		else if (!str_cmp(command, "-h") && !IS_NPC(ch) && last_type >= 0
				 && last_type < LAST_MAX)
		{
			chprintlnf(ch, "{WLast %d messages on %s{x", LAST_PAGE_LENGTH,
					   type);
			chprintln(ch, "{C------------------------------{x");
			view_last_data(ch, last_type);
			return;
		}
		else
		{
			chprintlnf(ch, "%s You say '%s'{x", type, arg_left);
			update_last_data(ch, ch, last_type, type, arg_left, CHANNEL_NORMAL);
		}
		for (d = descriptor_first; d != NULL; d = d->next)
		{
			CHAR_DATA *victim;

			if (d->connected != CON_PLAYING)
				continue;
			if ((victim = d->character) == NULL)
				continue;
			if (victim == ch)
				continue;
			if ((bitname && IS_SET(victim->comm, (bitname)))
				|| display_channel(ch, victim, spec_flag) == FALSE)
				continue;

			if (!display_wholist)
			{
				if (fEmote && !IS_SET(victim->comm, COMM_NOGOCIAL))
				{
					chprintlnf(victim, "%s %s %s{x", type,
							   smash_colour(PERS(ch, victim)), argument);
					update_last_data(ch, victim, last_type, type, argument,
									 CHANNEL_EMOTE);
				}
				else
				{
					chprintlnf(victim, "%s %s says '%s'{x", type,
							   smash_colour(PERS(ch, victim)), arg_left);
					update_last_data(ch, victim, last_type, type, arg_left,
									 CHANNEL_NORMAL);
				}
			}
			else
			{
				if (victim->invis_level < LEVEL_IMMORTAL
					&& victim->incog_level < LEVEL_IMMORTAL)
					chprintlnf(ch, "{W%s{x", PERS(victim, ch));
			}
		}
	}
}

CH_CMD(do_nogocial)
{
	if (IS_SET(ch->comm, COMM_NOGOCIAL))
	{
		REMOVE_BIT(ch->comm, COMM_NOGOCIAL);
		chprintln(ch, "You no longer see socials/emotes over channels.");
	}
	else
	{
		SET_BIT(ch->comm, COMM_NOGOCIAL);
		chprintln(ch, "You now see socials/emotes over channels.");
	}
}

/* RT code to display channel status */

CH_CMD(do_channels)
{

	/* lists all channels and their status */
	chprintln(ch, "   channel     status");
	chprintln(ch, "---------------------");

	chprint(ch, "gossip         ");
	if (!IS_SET(ch->comm, COMM_NOGOSSIP))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "ooc            ");
	if (!IS_SET(ch->comm, COMM_NOOOC))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "auction        ");
	if (!IS_SET(ch->comm, COMM_NOAUCTION))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "music          ");
	if (!IS_SET(ch->comm, COMM_NOMUSIC))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "Q/A            ");
	if (!IS_SET(ch->comm, COMM_NOQUESTION))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "Quote          ");
	if (!IS_SET(ch->comm, COMM_NOQUOTE))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "grats          ");
	if (!IS_SET(ch->comm, COMM_NOGRATS))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "btalk          ");
	if (!IS_SET(ch->comm, COMM_NOBUDDY))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	if (IS_IMMORTAL(ch))
	{
		chprint(ch, "god channel    ");
		if (!IS_SET(ch->comm, COMM_NOWIZ))
			chprintln(ch, "ON");
		else
			chprintln(ch, "OFF");
	}

	chprint(ch, "shouts         ");
	if (!IS_SET(ch->comm, COMM_SHOUTSOFF))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "tells          ");
	if (!IS_SET(ch->comm, COMM_DEAF))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	chprint(ch, "quiet mode     ");
	if (IS_SET(ch->comm, COMM_QUIET))
		chprintln(ch, "ON");
	else
		chprintln(ch, "OFF");

	if (IS_SET(ch->comm, COMM_AFK))
		chprintln(ch, "You are AFK.");

	if (IS_SET(ch->comm, COMM_SNOOP_PROOF))
		chprintln(ch, "You are immune to snooping.");

	if (ch->lines != PAGELEN)
	{
		if (ch->lines)
		{
			chprintlnf(ch, "You display %d lines of scroll.", ch->lines + 2);
		}
		else
			chprintln(ch, "Scroll buffering is off.");
	}

	if (ch->prompt != NULL)
	{
		chprintlnf(ch, "Your current prompt is: %s", ch->prompt);
	}

	if (IS_SET(ch->comm, COMM_NOSHOUT))
		chprintln(ch, "You cannot shout.");

	if (IS_SET(ch->comm, COMM_NOTELL))
		chprintln(ch, "You cannot use tell.");

	if (IS_SET(ch->comm, COMM_NOCHANNELS))
		chprintln(ch, "You cannot use channels.");

	if (IS_SET(ch->comm, COMM_NOEMOTE))
		chprintln(ch, "You cannot show emotions.");

	if (IS_SET(ch->comm, COMM_NOGOCIAL))
		chprintln(ch, "You cannot see socials/emotes over public channels.");

}

/* RT deaf blocks out all shouts */

CH_CMD(do_deaf)
{

	if (IS_SET(ch->comm, COMM_DEAF))
	{
		chprintln(ch, "You can now hear tells again.");
		REMOVE_BIT(ch->comm, COMM_DEAF);
	}
	else
	{
		chprintln(ch, "From now on, you won't hear tells.");
		SET_BIT(ch->comm, COMM_DEAF);
	}
}

/* RT quiet blocks out all communication */

CH_CMD(do_quiet)
{
	if (IS_SET(ch->comm, COMM_QUIET))
	{
		chprintln(ch, "Quiet mode removed.");
		REMOVE_BIT(ch->comm, COMM_QUIET);
	}
	else
	{
		chprintln(ch, "From now on, you will only hear says and emotes.");
		SET_BIT(ch->comm, COMM_QUIET);
	}
}

/* afk command */

CH_CMD(do_afk)
{
	if (IS_SET(ch->comm, COMM_AFK))
	{
		chprintln(ch, "AFK mode removed. Type 'replay' to see tells.");
		REMOVE_BIT(ch->comm, COMM_AFK);
	}
	else
	{
		chprintln(ch, "You are now in AFK mode.");
		SET_BIT(ch->comm, COMM_AFK);
	}
}

CH_CMD(do_replay)
{
	if (IS_NPC(ch))
	{
		chprintln(ch, "You can't replay.");
		return;
	}

	if (buf_string(ch->pcdata->buffer)[0] == '\0')
	{
		chprintln(ch, "You have no tells to replay.");
		return;
	}

	page_to_char(buf_string(ch->pcdata->buffer), ch);
	clear_buf(ch->pcdata->buffer);
}

/* RT chat replaced with ROM gossip */
CH_CMD(do_gossip)
{
	public_ch(ch, argument, CTAG(_GOSSIP1) "[Gossip]" CTAG(_GOSSIP2),
			  COMM_NOGOSSIP, spec_public_flag, LAST_GOSSIP);
}

CH_CMD(do_ooc)
{
	public_ch(ch, argument, "{C({WOOC{C){w", COMM_NOOOC, spec_public_flag,
			  LAST_OOC);
}

CH_CMD(do_grats)
{
	public_ch(ch, argument, CTAG(_GRATS1) "[Grats]" CTAG(_GRATS2), COMM_NOGRATS,
			  spec_public_flag, LAST_GRATS);
}

CH_CMD(do_quote)
{
	public_ch(ch, argument, CTAG(_QUOTE1) "[Quote]" CTAG(_QUOTE2), COMM_NOQUOTE,
			  spec_public_flag, LAST_QUOTE);
}

/* RT question channel */
CH_CMD(do_question)
{
	public_ch(ch, argument, CTAG(_QA1) "[Question]" CTAG(_QA2), COMM_NOQUESTION,
			  spec_public_flag, LAST_QA);
}

/* RT answer channel - uses same line as questions */
CH_CMD(do_answer)
{
	public_ch(ch, argument, CTAG(_QA1) "[Answer]" CTAG(_QA2), COMM_NOQUESTION,
			  spec_public_flag, LAST_QA);
}

/* RT music channel */
CH_CMD(do_music)
{
	public_ch(ch, argument,
			  CTAG(_MUSIC1) "[" CTAG(_MUSIC2) "MUSIC" CTAG(_MUSIC1) "]"
			  CTAG(_MUSIC3), COMM_NOMUSIC, spec_public_flag, LAST_MUSIC);
}

/* clan channels */
CH_CMD(do_clantalk)
{
	char buf[MSL];

	if (!is_clan(ch) || ch->clan->independent)
	{
		chprintln(ch, "You aren't in a clan.");
		return;
	}

	sprintf(buf, "%s{W (%s)", ch->clan->who_name,
			ch->clan->rank[ch->rank].rankname);
	public_ch(ch, argument, buf, COMM_NOCLAN, spec_clan_flag, LAST_CLANTALK);
}

CH_CMD(do_immtalk)
{
	public_ch(ch, argument,
			  CTAG(_IMMTALK1) "[" CTAG(_IMMTALK2) "ImmTalk" CTAG(_IMMTALK1) "]"
			  CTAG(_IMMTALK3), COMM_NOWIZ, spec_imm_flag, LAST_IMMTALK);
}

CH_CMD(do_say)
{
	if (argument[0] == '\0')
	{
		chprintln(ch, "Say what?");
		return;
	}

	act("" CTAG(_SAY1) "$n says '" CTAG(_SAY2) "$T" CTAG(_SAY1)
		"'{x", ch, NULL, argument, TO_ROOM);
	act("" CTAG(_SAY1) "You say '" CTAG(_SAY2) "$T" CTAG(_SAY1)
		"'{x", ch, NULL, argument, TO_CHAR);

	if (!IS_NPC(ch))
	{
		CHAR_DATA *mob, *mob_next;
		OBJ_DATA *obj, *obj_next;
		for (mob = ch->in_room->first_person; mob != NULL; mob = mob_next)
		{
			mob_next = mob->next_in_room;
			if (IS_NPC(mob) && HAS_TRIGGER_MOB(mob, TRIG_SPEECH) &&
				mob->position == mob->pIndexData->default_pos)
				p_act_trigger(argument, mob, NULL, NULL, ch, NULL, NULL,
							  TRIG_SPEECH);
			for (obj = mob->first_carrying; obj; obj = obj_next)
			{
				obj_next = obj->next_content;
				if (HAS_TRIGGER_OBJ(obj, TRIG_SPEECH))
					p_act_trigger(argument, NULL, obj, NULL, ch, NULL, NULL,
								  TRIG_SPEECH);
			}
		}
		for (obj = ch->in_room->first_content; obj; obj = obj_next)
		{
			obj_next = obj->next_content;
			if (HAS_TRIGGER_OBJ(obj, TRIG_SPEECH))
				p_act_trigger(argument, NULL, obj, NULL, ch, NULL, NULL,
							  TRIG_SPEECH);
		}

		if (HAS_TRIGGER_ROOM(ch->in_room, TRIG_SPEECH))
			p_act_trigger(argument, NULL, NULL, ch->in_room, ch, NULL, NULL,
						  TRIG_SPEECH);

	}

	return;
}

CH_CMD(do_shout)
{
	DESCRIPTOR_DATA *d;

	if (argument[0] == '\0')
	{
		if (IS_SET(ch->comm, COMM_SHOUTSOFF))
		{
			chprintln(ch, "You can hear shouts again.");
			REMOVE_BIT(ch->comm, COMM_SHOUTSOFF);
		}
		else
		{
			chprintln(ch, "You will no longer hear shouts.");
			SET_BIT(ch->comm, COMM_SHOUTSOFF);
		}
		return;
	}

	if (IS_SET(ch->comm, COMM_NOSHOUT))
	{
		chprintln(ch, "You can't shout.");
		return;
	}

	REMOVE_BIT(ch->comm, COMM_SHOUTSOFF);

	WAIT_STATE(ch, 12);

	act("" CTAG(_SHOUT1) "You shout '" CTAG(_SHOUT2) "$T"
		CTAG(_SHOUT1) "'{x", ch, NULL, argument, TO_CHAR);
	for (d = descriptor_first; d != NULL; d = d->next)
	{
		CHAR_DATA *victim;

		victim = d->original ? d->original : d->character;

		if (d->connected == CON_PLAYING && d->character != ch &&
			!IS_SET(victim->comm, COMM_SHOUTSOFF) &&
			!IS_SET(victim->comm, COMM_QUIET))
		{
			act("" CTAG(_SHOUT1) "$n shouts '" CTAG(_SHOUT2) "$t"
				CTAG(_SHOUT1) "'{x", ch, argument, d->character, TO_VICT);
		}
	}

	return;
}

CH_CMD(do_tell)
{
	char arg[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH];
	CHAR_DATA *victim;

	if (IS_SET(ch->comm, COMM_NOTELL) || IS_SET(ch->comm, COMM_DEAF))
	{
		chprintln(ch, "Your message didn't get through.");
		return;
	}

	if (IS_SET(ch->comm, COMM_QUIET))
	{
		chprintln(ch, "You must turn off quiet mode first.");
		return;
	}

	if (IS_SET(ch->comm, COMM_DEAF))
	{
		chprintln(ch, "You must turn off deaf mode first.");
		return;
	}

	argument = one_argument(argument, arg);

	if (arg[0] == '\0' || argument[0] == '\0')
	{
		chprintln(ch, "Tell whom what?");
		return;
	}

	/*
	 * Can tell to PC's anywhere, but NPC's only in same room.
	 * -- Furey
	 */
	if ((victim = get_char_world(ch, arg)) == NULL ||
		(IS_NPC(victim) && victim->in_room != ch->in_room))
	{
		chprintln(ch, "They aren't here.");
		return;
	}

	if (victim->desc == NULL && !IS_NPC(victim))
	{
		act("$N seems to have misplaced $S link...try again later.", ch,
			NULL, victim, TO_CHAR);
		sprintf(buf, "%s tells you '%s'\n\r", PERS(ch, victim), argument);
		buf[0] = UPPER(buf[0]);
		add_buf(victim->pcdata->buffer, buf);
		return;
	}

	if (!(IS_IMMORTAL(ch) && ch->level > LEVEL_IMMORTAL) && !IS_AWAKE(victim))
	{
		act("$E can't hear you.", ch, 0, victim, TO_CHAR);
		return;
	}

	if ((IS_SET(victim->comm, COMM_QUIET) ||
		 IS_SET(victim->comm, COMM_DEAF)) && !IS_IMMORTAL(ch))
	{
		act("$E is not receiving tells.", ch, 0, victim, TO_CHAR);
		return;
	}

	if (IS_SET(victim->comm, COMM_AFK))
	{
		if (IS_NPC(victim))
		{
			act("$E is AFK, and not receiving tells.", ch, NULL,
				victim, TO_CHAR);
			return;
		}

		act("$E is AFK, but your tell will go through when $E returns.",
			ch, NULL, victim, TO_CHAR);
		sprintf(buf, "%s tells you '%s'\n\r", PERS(ch, victim), argument);
		buf[0] = UPPER(buf[0]);
		add_buf(victim->pcdata->buffer, buf);
		return;
	}

	act("" CTAG(_TELLS1) "You tell $N '" CTAG(_TELLS2) "$t"
		CTAG(_TELLS1) "'{x", ch, argument, victim, TO_CHAR);
	act_new("" CTAG(_TELLS1) "$n tells you '" CTAG(_TELLS2) "$t"
			CTAG(_TELLS1) "'{x", ch, argument, victim, TO_VICT, POS_DEAD);
	victim->reply = ch;

	if (!IS_NPC(ch) && IS_NPC(victim) && HAS_TRIGGER_MOB(victim, TRIG_SPEECH))
		p_act_trigger(argument, victim, NULL, NULL, ch, NULL, NULL,
					  TRIG_SPEECH);

	return;
}

CH_CMD(do_reply)
{
	CHAR_DATA *victim;
	char buf[MAX_STRING_LENGTH];

	if (IS_SET(ch->comm, COMM_NOTELL))
	{
		chprintln(ch, "Your message didn't get through.");
		return;
	}

	if ((victim = ch->reply) == NULL)
	{
		chprintln(ch, "They aren't here.");
		return;
	}

	if (victim->desc == NULL && !IS_NPC(victim))
	{
		act("$N seems to have misplaced $S link...try again later.", ch,
			NULL, victim, TO_CHAR);
		sprintf(buf, "%s tells you '%s'\n\r", PERS(ch, victim), argument);
		buf[0] = UPPER(buf[0]);
		add_buf(victim->pcdata->buffer, buf);
		return;
	}

	if (!IS_IMMORTAL(ch) && !IS_AWAKE(victim))
	{
		act("$E can't hear you.", ch, 0, victim, TO_CHAR);
		return;
	}

	if ((IS_SET(victim->comm, COMM_QUIET) ||
		 IS_SET(victim->comm, COMM_DEAF)) && !IS_IMMORTAL(ch) &&
		!IS_IMMORTAL(victim))
	{
		act_new("$E is not receiving tells.", ch, 0, victim, TO_CHAR, POS_DEAD);
		return;
	}

	if (!IS_IMMORTAL(victim) && !IS_AWAKE(ch))
	{
		chprintln(ch, "In your dreams, or what?");
		return;
	}

	if (IS_SET(victim->comm, COMM_AFK))
	{
		if (IS_NPC(victim))
		{
			act_new("$E is AFK, and not receiving tells.", ch, NULL,
					victim, TO_CHAR, POS_DEAD);
			return;
		}

		act_new
			("$E is AFK, but your tell will go through when $E returns.",
			 ch, NULL, victim, TO_CHAR, POS_DEAD);
		sprintf(buf, "%s tells you '%s'\n\r", PERS(ch, victim), argument);
		buf[0] = UPPER(buf[0]);
		add_buf(victim->pcdata->buffer, buf);
		return;
	}

	act_new("You tell $N '$t'", ch, argument, victim, TO_CHAR, POS_DEAD);
	act_new("$n tells you '$t'", ch, argument, victim, TO_VICT, POS_DEAD);
	victim->reply = ch;

	return;
}

CH_CMD(do_yell)
{
	DESCRIPTOR_DATA *d;

	if (IS_SET(ch->comm, COMM_NOSHOUT))
	{
		chprintln(ch, "You can't yell.");
		return;
	}

	if (argument[0] == '\0')
	{
		chprintln(ch, "Yell what?");
		return;
	}

	act("You yell '$t'", ch, argument, NULL, TO_CHAR);
	for (d = descriptor_first; d != NULL; d = d->next)
	{
		if (d->connected == CON_PLAYING && d->character != ch &&
			d->character->in_room != NULL &&
			d->character->in_room->area == ch->in_room->area &&
			!IS_SET(d->character->comm, COMM_QUIET))
		{
			act("$n yells '$t'", ch, argument, d->character, TO_VICT);
		}
	}

	return;
}

CH_CMD(do_emote)
{
	if (!IS_NPC(ch) && IS_SET(ch->comm, COMM_NOEMOTE))
	{
		chprintln(ch, "You can't show your emotions.");
		return;
	}

	if (argument[0] == '\0')
	{
		chprintln(ch, "Emote what?");
		return;
	}

	MOBtrigger = FALSE;
	act("$n $T", ch, NULL, argument, TO_ROOM);
	act("$n $T", ch, NULL, argument, TO_CHAR);
	MOBtrigger = TRUE;
	return;
}

CH_CMD(do_pmote)
{
	CHAR_DATA *vch;
	const char *letter, *name;
	char last[MAX_INPUT_LENGTH], temp[MAX_STRING_LENGTH];
	unsigned int matches = 0;

	if (!IS_NPC(ch) && IS_SET(ch->comm, COMM_NOEMOTE))
	{
		chprintln(ch, "You can't show your emotions.");
		return;
	}

	if (argument[0] == '\0')
	{
		chprintln(ch, "Emote what?");
		return;
	}

	act("$n $t", ch, argument, NULL, TO_CHAR);

	for (vch = ch->in_room->first_person; vch != NULL; vch = vch->next_in_room)
	{
		if (vch->desc == NULL || vch == ch)
			continue;

		if ((letter = strstr(argument, vch->name)) == NULL)
		{
			MOBtrigger = FALSE;
			act("$N $t", vch, argument, ch, TO_CHAR);
			MOBtrigger = TRUE;
			continue;
		}

		strcpy(temp, argument);
		temp[strlen(argument) - strlen(letter)] = '\0';
		last[0] = '\0';
		name = vch->name;

		for (; *letter != '\0'; letter++)
		{
			if (*letter == '\'' && matches == strlen(vch->name))
			{
				strcat(temp, "r");
				continue;
			}

			if (*letter == 's' && matches == strlen(vch->name))
			{
				matches = 0;
				continue;
			}

			if (matches == strlen(vch->name))
			{
				matches = 0;
			}

			if (*letter == *name)
			{
				matches++;
				name++;
				if (matches == strlen(vch->name))
				{
					strcat(temp, "you");
					last[0] = '\0';
					name = vch->name;
					continue;
				}
				strncat(last, letter, 1);
				continue;
			}

			matches = 0;
			strcat(temp, last);
			strncat(temp, letter, 1);
			last[0] = '\0';
			name = vch->name;
		}

		MOBtrigger = FALSE;
		act("$N $t", vch, temp, ch, TO_CHAR);
		MOBtrigger = TRUE;
	}

	return;
}

CH_CMD(do_bug)
{
	append_file(ch, BUG_FILE, argument);
	chprintln(ch, "Bug logged.");
	return;
}

CH_CMD(do_typo)
{
	append_file(ch, TYPO_FILE, argument);
	chprintln(ch, "Typo logged.");
	return;
}

CH_CMD(do_rent)
{
	chprintln(ch, "There is no rent here.  Just save and quit.");
	return;
}

CH_CMD(do_qui)
{
	chprintln(ch, "If you want to QUIT, you have to spell it out.");
	return;
}

CH_CMD(do_quit)
{
	DESCRIPTOR_DATA *d, *d_next;
	int id;

	if (IS_NPC(ch))
		return;

	if (ch->position == POS_FIGHTING)
	{
		chprintln(ch, "No way! You are fighting.");
		return;
	}

	if (ch->position < POS_STUNNED)
	{
		chprintln(ch, "You're not DEAD yet.");
		return;
	}
	if (has_auction(ch))
	{
		chprintln
			(ch,
			 "{YThe Auctioneer will not let you leave while you have an auction running.{x");
		return;
	}

	chprintln(ch, "Alas, all good things must come to an end.");
	announce(ch, INFO_LOGOUT, "$n has left the realms.");
	act("$n has left the game.", ch, NULL, NULL, TO_ROOM);
	sprintf(log_buf, "%s has quit.", ch->name);
	log_string(log_buf);
	wiznet("$N rejoins the real world.", ch, NULL, WIZ_LOGINS, 0,
		   get_trust(ch));

	/*
	 * After extract_char the ch is no longer valid!
	 */
	save_char_obj(ch);

	/* Free note that might be there somehow */
	if (ch->pcdata->in_progress)
		free_note(ch->pcdata->in_progress);

	id = ch->id;
	d = ch->desc;
	extract_char(ch, TRUE);
	if (d != NULL)
		close_socket(d);

	/* toast evil cheating bastards */
	for (d = descriptor_first; d != NULL; d = d_next)
	{
		CHAR_DATA *tch;

		d_next = d->next;
		tch = d->original ? d->original : d->character;
		if (tch && tch->id == id)
		{
			extract_char(tch, TRUE);
			close_socket(d);
		}
	}

	return;
}

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

	save_char_obj(ch);
	update_statlist(ch, FALSE);
	if (is_clan(ch))
		update_members(ch, FALSE);
	chprintln(ch, "Saving. Remember that ROM has automatic saving now.");
	WAIT_STATE(ch, PULSE_VIOLENCE);
	return;
}

CH_CMD(do_follow)
{
/* RT changed to allow unlimited following and follow the NOFOLLOW rules */
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;

	one_argument(argument, arg);

	if (arg[0] == '\0')
	{
		chprintln(ch, "Follow whom?");
		return;
	}

	if ((victim = get_char_room(ch, NULL, arg)) == NULL)
	{
		chprintln(ch, "They aren't here.");
		return;
	}

	if (IS_AFFECTED(ch, AFF_CHARM) && ch->master != NULL)
	{
		act("But you'd rather follow $N!", ch, NULL, ch->master, TO_CHAR);
		return;
	}

	if (victim == ch)
	{
		if (ch->master == NULL)
		{
			chprintln(ch, "You already follow yourself.");
			return;
		}
		stop_follower(ch);
		return;
	}

	if (!IS_NPC(victim) && IS_SET(victim->act, PLR_NOFOLLOW) &&
		!IS_IMMORTAL(ch))
	{
		act("$N doesn't seem to want any followers.\n\r", ch, NULL,
			victim, TO_CHAR);
		return;
	}

	REMOVE_BIT(ch->act, PLR_NOFOLLOW);

	if (ch->master != NULL)
		stop_follower(ch);

	add_follower(ch, victim);
	return;
}

void add_follower(CHAR_DATA * ch, CHAR_DATA * master)
{
	if (ch->master != NULL)
	{
		bug("Add_follower: non-null master.", 0);
		return;
	}

	ch->master = master;
	ch->leader = NULL;

	if (can_see(master, ch))
		act("$n now follows you.", ch, NULL, master, TO_VICT);

	act("You now follow $N.", ch, NULL, master, TO_CHAR);

	return;
}

void stop_follower(CHAR_DATA * ch)
{
	if (ch->master == NULL)
	{
		bug("Stop_follower: null master.", 0);
		return;
	}

	if (IS_AFFECTED(ch, AFF_CHARM))
	{
		REMOVE_BIT(ch->affected_by, AFF_CHARM);
		affect_strip(ch, gsn_charm_person);
	}

	if (can_see(ch->master, ch) && ch->in_room != NULL)
	{
		act("$n stops following you.", ch, NULL, ch->master, TO_VICT);
		act("You stop following $N.", ch, NULL, ch->master, TO_CHAR);
	}
	if (ch->master->pet == ch)
		ch->master->pet = NULL;

	ch->master = NULL;
	ch->leader = NULL;
	return;
}

/* nukes charmed monsters and pets */
void nuke_pets(CHAR_DATA * ch)
{
	CHAR_DATA *pet;

	if ((pet = ch->pet) != NULL)
	{
		stop_follower(pet);
		if (pet->in_room != NULL)
			act("$N slowly fades away.", ch, NULL, pet, TO_NOTVICT);
		extract_char(pet, TRUE);
	}
	ch->pet = NULL;

	return;
}

void die_follower(CHAR_DATA * ch)
{
	CHAR_DATA *fch;

	if (ch->master != NULL)
	{
		if (ch->master->pet == ch)
			ch->master->pet = NULL;
		stop_follower(ch);
	}

	ch->leader = NULL;

	for (fch = char_first; fch != NULL; fch = fch->next)
	{
		if (fch->master == ch)
			stop_follower(fch);
		if (fch->leader == ch)
			fch->leader = fch;
	}

	return;
}

CH_CMD(do_order)
{
	char buf[MAX_STRING_LENGTH];
	char arg[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	CHAR_DATA *och;
	CHAR_DATA *och_next;
	bool found;
	bool fAll;

	argument = one_argument(argument, arg);
	one_argument(argument, arg2);

	if (!str_cmp(arg2, "delete") || !str_cmp(arg2, "mob"))
	{
		chprintln(ch, "That will NOT be done.");
		return;
	}

	if (arg[0] == '\0' || argument[0] == '\0')
	{
		chprintln(ch, "Order whom to do what?");
		return;
	}

	if (IS_AFFECTED(ch, AFF_CHARM))
	{
		chprintln(ch, "You feel like taking, not giving, orders.");
		return;
	}

	if (!str_cmp(arg, "all"))
	{
		fAll = TRUE;
		victim = NULL;
	}
	else
	{
		fAll = FALSE;
		if ((victim = get_char_room(ch, NULL, arg)) == NULL)
		{
			chprintln(ch, "They aren't here.");
			return;
		}

		if (victim == ch)
		{
			chprintln(ch, "Aye aye, right away!");
			return;
		}

		if (!IS_AFFECTED(victim, AFF_CHARM) || victim->master != ch ||
			(IS_IMMORTAL(victim) && victim->trust >= ch->trust))
		{
			chprintln(ch, "Do it yourself!");
			return;
		}
	}

	found = FALSE;
	for (och = ch->in_room->first_person; och != NULL; och = och_next)
	{
		och_next = och->next_in_room;

		if (IS_AFFECTED(och, AFF_CHARM) && och->master == ch &&
			(fAll || och == victim))
		{
			found = TRUE;
			sprintf(buf, "$n orders you to '%s'.", argument);
			act(buf, ch, NULL, och, TO_VICT);
			interpret(och, argument);
		}
	}

	if (found)
	{
		WAIT_STATE(ch, PULSE_VIOLENCE);
		chprintln(ch, "Ok.");
	}
	else
		chprintln(ch, "You have no followers here.");
	return;
}

CH_CMD(do_group)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;

	one_argument(argument, arg);

	if (arg[0] == '\0')
	{
		CHAR_DATA *gch;
		CHAR_DATA *leader;

		leader = (ch->leader != NULL) ? ch->leader : ch;
		chprintlnf(ch, "%s's group:", PERS(leader, ch));

		for (gch = char_first; gch != NULL; gch = gch->next)
		{
			if (is_same_group(gch, ch))
			{
				chprintlnf(ch,
						   "[%2d %s] %-16s %4ld/%4ld hp %4ld/%4ld mana %4ld/%4ld mv %5d xp",
						   gch->level,
						   IS_NPC(gch) ? "Mob" : class_who(gch),
						   capitalize(PERS(gch, ch)), gch->hit,
						   gch->max_hit, gch->mana, gch->max_mana,
						   gch->move, gch->max_move, gch->exp);
			}
		}
		return;
	}

	if ((victim = get_char_room(ch, NULL, arg)) == NULL)
	{
		chprintln(ch, "They aren't here.");
		return;
	}

	if (ch->master != NULL || (ch->leader != NULL && ch->leader != ch))
	{
		chprintln(ch, "But you are following someone else!");
		return;
	}

	if (victim->master != ch && ch != victim)
	{
		act_new("$N isn't following you.", ch, NULL, victim, TO_CHAR,
				POS_SLEEPING);
		return;
	}

	if (IS_AFFECTED(victim, AFF_CHARM))
	{
		chprintln(ch, "You can't remove charmed mobs from your group.");
		return;
	}

	if (IS_AFFECTED(ch, AFF_CHARM))
	{
		act_new("You like your master too much to leave $m!", ch, NULL,
				victim, TO_VICT, POS_SLEEPING);
		return;
	}

	if (is_same_group(victim, ch) && ch != victim)
	{
		victim->leader = NULL;
		act_new("$n removes $N from $s group.", ch, NULL, victim,
				TO_NOTVICT, POS_RESTING);
		act_new("$n removes you from $s group.", ch, NULL, victim,
				TO_VICT, POS_SLEEPING);
		act_new("You remove $N from your group.", ch, NULL, victim,
				TO_CHAR, POS_SLEEPING);
		return;
	}

	victim->leader = ch;
	act_new("$N joins $n's group.", ch, NULL, victim, TO_NOTVICT, POS_RESTING);
	act_new("You join $n's group.", ch, NULL, victim, TO_VICT, POS_SLEEPING);
	act_new("$N joins your group.", ch, NULL, victim, TO_CHAR, POS_SLEEPING);
	return;
}

/*
 * 'Split' originally by Gnort, God of Chaos.
 */
CH_CMD(do_split)
{
	char buf[MAX_STRING_LENGTH];
	char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
	CHAR_DATA *gch;
	int members;
	int amount_gold = 0, amount_silver = 0;
	int share_gold, share_silver;
	int extra_gold, extra_silver;

	argument = one_argument(argument, arg1);
	one_argument(argument, arg2);

	if (arg1[0] == '\0')
	{
		chprintln(ch, "Split how much?");
		return;
	}

	amount_silver = atoi(arg1);

	if (arg2[0] != '\0')
		amount_gold = atoi(arg2);

	if (amount_gold < 0 || amount_silver < 0)
	{
		chprintln(ch, "Your group wouldn't like that.");
		return;
	}

	if (amount_gold == 0 && amount_silver == 0)
	{
		chprintln(ch, "You hand out zero coins, but no one notices.");
		return;
	}

	if (ch->gold < amount_gold || ch->silver < amount_silver)
	{
		chprintln(ch, "You don't have that much to split.");
		return;
	}

	members = 0;
	for (gch = ch->in_room->first_person; gch != NULL; gch = gch->next_in_room)
	{
		if (is_same_group(gch, ch) && !IS_AFFECTED(gch, AFF_CHARM))
			members++;
	}

	if (members < 2)
	{
		chprintln(ch, "Just keep it all.");
		return;
	}

	share_silver = amount_silver / members;
	extra_silver = amount_silver % members;

	share_gold = amount_gold / members;
	extra_gold = amount_gold % members;

	if (share_gold == 0 && share_silver == 0)
	{
		chprintln(ch, "Don't even bother, cheapskate.");
		return;
	}

	ch->silver -= amount_silver;
	ch->silver += share_silver + extra_silver;
	ch->gold -= amount_gold;
	ch->gold += share_gold + extra_gold;

	if (share_silver > 0)
	{
		chprintlnf(ch,
				   "You split %d silver coins. Your share is %d silver.",
				   amount_silver, share_silver + extra_silver);
	}

	if (share_gold > 0)
	{
		chprintlnf(ch,
				   "You split %d gold coins. Your share is %d gold.",
				   amount_gold, share_gold + extra_gold);
	}

	if (share_gold == 0)
	{
		sprintf(buf,
				"$n splits %d silver coins. Your share is %d silver.",
				amount_silver, share_silver);
	}
	else if (share_silver == 0)
	{
		sprintf(buf, "$n splits %d gold coins. Your share is %d gold.",
				amount_gold, share_gold);
	}
	else
	{
		sprintf(buf,
				"$n splits %d silver and %d gold coins, giving you %d silver and %d gold.\n\r",
				amount_silver, amount_gold, share_silver, share_gold);
	}

	for (gch = ch->in_room->first_person; gch != NULL; gch = gch->next_in_room)
	{
		if (gch != ch && is_same_group(gch, ch) && !IS_AFFECTED(gch, AFF_CHARM))
		{
			act(buf, ch, NULL, gch, TO_VICT);
			gch->gold += share_gold;
			gch->silver += share_silver;
		}
	}

	return;
}

CH_CMD(do_gtell)
{
	CHAR_DATA *gch;

	if (argument[0] == '\0')
	{
		chprintln(ch, "Tell your group what?");
		return;
	}

	if (IS_SET(ch->comm, COMM_NOTELL))
	{
		chprintln(ch, "Your message didn't get through!");
		return;
	}

	for (gch = char_first; gch != NULL; gch = gch->next)
	{
		if (is_same_group(gch, ch))
			act_new("" CTAG(_GTELL1) "$n tells the group '"
					CTAG(_GTELL2) "$t" CTAG(_GTELL1) "'{x", ch,
					argument, gch, TO_VICT, POS_SLEEPING);
	}

	return;
}

/*
 * It is very important that this be an equivalence relation:
 * (1) A ~ A
 * (2) if A ~ B then B ~ A
 * (3) if A ~ B  and B ~ C, then A ~ C
 */
bool is_same_group(CHAR_DATA * ach, CHAR_DATA * bch)
{
	if (ach == NULL || bch == NULL)
		return FALSE;

	if (ach->leader != NULL)
		ach = ach->leader;
	if (bch->leader != NULL)
		bch = bch->leader;
	return ach == bch;
}

/* Turns colour on and sets the appropriate flags */
CH_CMD(do_colour)
{
	if (!ch)
		return;

	if (IS_NPC(ch) || ch->desc == NULL)
	{
		chprintln(ch, "ColoUr is not ON, Way Moron!");
		return;
	}
	if (!DESC_FLAGGED(ch->desc, DESC_COLOUR))
	{
		SET_BIT(ch->desc->d_flags, DESC_COLOUR);
		REMOVE_BIT(ch->comm, COMM_NOCOLOUR);
		chprintln
			(ch, "{`Co{`lo{`U{`r {`i{`s {`n{`o{`w {`ON{`, W{`a{`y C{`oo{`l!{x");
	}
	else
	{
		chprintln(ch, "ColoUr is now OFF, <sigh>");
		REMOVE_BIT(ch->desc->d_flags, DESC_COLOUR);
		SET_BIT(ch->comm, COMM_NOCOLOUR);
	}
	return;
}

int find_colour_slot(const char *code)
{
	int i;

	for (i = 0; colour_chars[i].code != NULL; i++)
		if (!str_cmp(code, colour_chars[i].code))
			return i;

	return 0;
}

/* Sets all or 1 custom colours to default */
void default_colour(CHAR_DATA * ch, int slot)
{
	int i = 0;

	if (!ch || IS_NPC(ch))
		return;

	if (slot == -1)
	{
		for (i = 0; cslot_table[i].def != NULL; i++)
			ch->pcdata->colour[cslot_table[i].slot] =
				find_colour_slot(cslot_table[i].def);
	}
	else
	{
		for (i = 0; cslot_table[i].def != NULL; i++)
		{
			if (cslot_table[i].slot == slot)
			{
				ch->pcdata->colour[slot] = find_colour_slot(cslot_table[i].def);
				break;
			}
		}
	}

	return;
}

const char *color_default(CHAR_DATA * ch)
{
	int colour;

	if (!ch || IS_NPC(ch))
		return C_CLEAR;

	colour = ch->pcdata->colour[_DEFAULT];

	if (colour < 0 || colour_chars[colour].code == NULL)
		return C_CLEAR;

	else if (!str_cmp(colour_chars[colour].code, C_RANDOM))
		return random_colour();

	else if (!str_cmp(colour_chars[colour].code, RANDOM_BG))
		return random_background();

	else
		return colour_chars[ch->pcdata->colour[_DEFAULT]].code;
}

/* return the players ansi code for a slot */
const char *char_colour(CHAR_DATA * ch, int slot)
{
	int colour;

	if (!ch || IS_NPC(ch) || slot < 0 || slot > MAX_CUSTOM_COLOUR)
		return C_CLEAR;

	colour = ch->pcdata->colour[slot];

	if (colour < 0 || colour_chars[colour].code == NULL)
		return C_CLEAR;

	else if (!str_cmp(colour_chars[colour].code, C_RANDOM))
		return random_colour();

	else if (!str_cmp(colour_chars[colour].code, RANDOM_BG))
		return random_background();

	else
		return colour_chars[colour].code;

}

/* return a colour number */
int colour_lookup(const char *arg)
{
	int i;

	for (i = 0; colour_chars[i].name != NULL; i++)
		if (!str_cmp(arg, colour_chars[i].name))
			return i;

	return -1;
}

/* returns a custom colour number */
int cslot_lookup(const char *arg)
{
	int i;

	for (i = 0; cslot_table[i].name != NULL; i++)
		if (!str_prefix(arg, cslot_table[i].name))
			return cslot_table[i].slot;

	return -1;
}

/* sets custom colours */
CH_CMD(do_colourset)
{
	char arg[MIL], arg2[MIL];
	int i = 0, pos = 0, slot = 0, color = 0;

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

	if (!DESC_FLAGGED(ch->desc, DESC_COLOUR))
	{
		chprintln(ch, "You must have colour on to use colourset.");
		return;
	}

	argument = one_argument(argument, arg);
	argument = one_argument(argument, arg2);

	if (IS_NULLSTR(arg))
	{
		chprintln
			(ch,
			 "Syntax: colourset colours           - lists possible colours");
		chprintln(ch,
				  "        colourset options           - lists options to colourize");
		chprintln(ch,
				  "        colourset <option> <colour> - sets an option to a colour");
		chprintln(ch,
				  "        colourset default <option>  - sets option to default value");
		chprintln(ch,
				  "        colourset default all       - reset all options to default values");
		return;
	}
	if (!str_prefix(arg, "colours") || !str_prefix(arg, "colors"))
	{
		chprintln(ch, "Available Colours:");
		for (i = 0; colour_chars[i].name != NULL; i++)
		{
			chprintf(ch, "%s%-10s{x", colour_chars[i].code,
					 colour_chars[i].name);
			if (++pos % 4 == 0)
			{
				chprintln(ch, "");
				pos = 0;
			}
		}
		if (pos != 0)
			chprintln(ch, "");
	}
	else if (!str_prefix(arg, "options"))
	{
		chprintln(ch, "Available Options:");
		for (i = 0; cslot_table[i].name != NULL; i++)
		{
			chprintf(ch, "" CTAG(%d) "%-10s{x", cslot_table[i].slot,
					 cslot_table[i].name);
			if (++pos % 6 == 0)
			{
				chprintln(ch, "");
				pos = 0;
			}
		}
		if (pos != 0)
			chprintln(ch, "");
	}
	else if (!str_prefix(arg, "default"))
	{
		if ((slot = cslot_lookup(arg2)) == -1)
		{
			if (!str_cmp(arg2, "all"))
				chprintln(ch, "All colour values set to default.");
			else
			{
				do_colourset(ch, "");
				return;
			}
		}
		else
			chprintf(ch, "%s set to default value.\n\r",
					 cslot_table[slot].name);
		default_colour(ch, slot);
		return;
	}
	else
	{
		if ((slot = cslot_lookup(arg)) == -1)
		{
			chprintln(ch, "Invalid Option.");
			return;
		}

		if ((color = colour_lookup(arg2)) == -1)
		{
			chprintln(ch, "Invalid Colour.");
			return;
		}

		ch->pcdata->colour[slot] = color;
		chprintf(ch, "%s%s{x set to %s%s{x.\n\r", char_colour(ch, slot),
				 cslot_table[slot].name, char_colour(ch, slot),
				 colour_chars[color].name);
		return;
	}
}

void announce(CHAR_DATA * ch, flag_t bit, const char *message, ...)
{
	DESCRIPTOR_DATA *d;
	char buf[MSL], buf2[MSL];
	char *iType;
	bool Private = FALSE;
	va_list args;

	va_start(args, message);
	vsnprintf(buf2, MSL, message, args);
	va_end(args);

	if (ch && IS_SET(bit, INFO_PRIVATE))
	{
		Private = TRUE;
		REMOVE_BIT(bit, INFO_PRIVATE);
	}

	if (bit == INFO_NOTE)
		iType = "{GBOARD";
	else if (bit == INFO_WAR)
		iType = "{CWAR";
	else if (bit == INFO_GQUEST)
		iType = "{MGQUEST";
	else if (bit == INFO_AUCTION)
		iType = "{YAUCTION";
	else if (bit == INFO_ARENA)
		iType = "{BARENA";
	else
		iType = "{RINFO";

	sprintf(buf, "%s{W:{x %s{x", iType, buf2);

	if (!Private)
	{
		for (d = descriptor_first; d; d = d->next)
		{
			CHAR_DATA *och;

			if ((och = d->character) == NULL)
				continue;
			if (d->connected != CON_PLAYING)
				continue;
			if (bit == INFO_NOTE && ch &&
				!is_note_to(och, ch->pcdata->in_progress))
				continue;
			if ((IS_SET(och->info_settings, bit) ||
				 !IS_SET(och->info_settings, INFO_QUIET)) && bit != INFO_ALL)
				continue;

			if (ch == NULL)
				chprintf(och, "%s\n\r", buf);
			else
				act_new(buf, ch, NULL, och, TO_VICT, POS_DEAD);
		}
	}
	else
	{
		if (!ch)
		{
			bug("NULL ch in private announce", 0);
			return;
		}
		chprintf(ch, "%s\n\r", buf);
	}
	return;
}

CH_CMD(do_info)
{
	int i;
	flag_t iValue;

	if (!*argument || !str_cmp(argument, "list"))
	{
		chprintln(ch, "{RInformation settings:");
		chprintln(ch, "{r---------------------{x");
		for (i = 0; info_flags[i].name != NULL; i++)
		{
			chprintf(ch, "%-13.25s [%s]\n\r",
					 info_flags[i].name,
					 IS_SET(ch->info_settings,
							info_flags[i].bit) ? "{WOFF{x" : " {RON{x");
		}
		return;
	}

	if ((iValue = flag_value(info_flags, argument)) > 0)
	{
		TOGGLE_BIT(ch->info_settings, iValue);
		chprintln(ch, "Info channel set.");
		return;
	}
	chprintln(ch, "Which option??");
	return;
}

CH_CMD(do_announce)
{
	announce(NULL, INFO_ALL, argument);
}