zim/area/
zim/bin/
zim/clans/plists/
zim/corefiles/
zim/doc/muddy/
zim/gods/
zim/log/
zim/player/
zim/skill_tree/
zim/tmp/
/*
 * $Id: act_comm.c 1019 2007-02-15 00:52:41Z zsuzsu $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR  *	
 *     ANATOLIA has been brought to you by ANATOLIA consortium		   *
 *	 Serdar BULUT {Chronos}		bulut@rorqual.cc.metu.edu.tr       *	
 *	 Ibrahim Canpunar  {Asena}	canpunar@rorqual.cc.metu.edu.tr    *	
 *	 Murat BICER  {KIO}		mbicer@rorqual.cc.metu.edu.tr	   *	
 *	 D.Baris ACAR {Powerman}	dbacar@rorqual.cc.metu.edu.tr	   *	
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *	
 ***************************************************************************/

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments 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          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 **************************************************************************/

/***************************************************************************
*	ROM 2.4 is copyright 1993-1995 Russ Taylor			   *
*	ROM has been brought to you by the ROM consortium		   *
*	    Russ Taylor (rtaylor@pacinfo.com)				   *
*	    Gabrielle Taylor (gtaylor@pacinfo.com)			   *
*	    Brian Moore (rom@rom.efn.org)				   *
*	By using this code, you have agreed to follow the terms of the	   *
*	ROM license, in the file Rom24/doc/rom.license			   *
***************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#if	!defined (WIN32)
#include <unistd.h>
#endif

#include "merc.h"
#include "debug.h"
#include "fight.h"
#include "quest.h"
#include "mob_prog.h"
#include "obj_prog.h"
#include "auction.h"
#include "db/lang.h"
#include "db/gsn.h"
#include "channels.h"

/* command procedures needed */
DECLARE_DO_FUN(do_replay);
DECLARE_DO_FUN(do_look);

void do_afk(CHAR_DATA * ch, const char *argument)
{
	if (!IS_IMMORTAL(ch)
	    && !IS_SET(ch->comm, COMM_AFK)) {
		if (ch->in_room != NULL
		    && !IS_SET(ch->in_room->room_flags, ROOM_SAFE))
			char_puts
			    ("Going {cAFK{x does not prevent you from being"
			     " {RPK{xed, in fact some people take it as an"
			     " opportunity to get an easy kill.  It would be much"
			     " wiser if you found a safe room first.\n", ch);
		else
			char_puts
			    ("Ah, going {cAFK{x in a safe room, very wise!\n",
			     ch);
	}

	if (IS_SET(ch->comm, COMM_AFK))
		do_replay(ch, str_empty);
	else
		char_puts("You are now in {cAFK{x mode.\n", ch);

	TOGGLE_BIT(ch->comm, COMM_AFK);
}

/* RT code to delete yourself */

void do_delet(CHAR_DATA * ch, const char *argument)
{
	char_puts("You must type the full command to delete yourself.\n", ch);
}

void do_delete(CHAR_DATA * ch, const char *argument)
{
	if (IS_NPC(ch))
		return;

	if (IS_SET(ch->acct_flags, ACCT_DELETING)) {
		if (argument[0] != '\0') {
			char_puts("Delete status removed.\n", ch);
			REMOVE_BIT(ch->acct_flags, ACCT_DELETING);
			return;
		}

		wiznet("$N turns $Mself into line noise.", ch, NULL, 0, 0, 0);
		delete_player(ch, NULL);
		return;
	}

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

	char_puts("Type delete again to confirm this command.\n"
		  "WARNING: this command is irreversible.\n"
		  "Typing delete with an argument will undo delete status.\n",
		  ch);
	SET_BIT(ch->acct_flags, ACCT_DELETING);
	wiznet("$N is contemplating deletion.", ch, NULL, 0, 0, ch->level);
}

void do_deaf(CHAR_DATA * ch, const char *argument)
{
	if (IS_SET(ch->comm, COMM_DEAF)) {
		char_puts("You can now hear tells again.\n", ch);
		REMOVE_BIT(ch->comm, COMM_DEAF);
	} else {
		char_puts("From now on, you won't hear tells.\n", ch);
		SET_BIT(ch->comm, COMM_DEAF);
	}
}

/* RT quiet blocks out all communication */

void do_quiet(CHAR_DATA * ch, const char *argument)
{
	if (IS_SET(ch->comm, COMM_QUIET)) {
		char_puts("Quiet mode removed.\n", ch);
		REMOVE_BIT(ch->comm, COMM_QUIET);
	} else {
		char_puts("From now on, you will only hear says and emotes.\n",
			  ch);
		SET_BIT(ch->comm, COMM_QUIET);
	}
}

void do_replay(CHAR_DATA * ch, const char *argument)
{
	if (IS_NPC(ch)) {
		char_puts("You can't replay.\n", ch);
		return;
	}

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

void do_whisper(CHAR_DATA * ch, const char *argument)
{
	OBJ_DATA *char_obj;
	OBJ_DATA *char_obj_next;

	if (ch->position == POS_SLEEPING) {
		char_puts("In your dreams, or what?\n", ch);
		return;
	}

	if (argument[0] == '\0') {
		char_puts("Whisper what?\n", ch);
		return;
	}

	argument = garble(ch, argument);
	act_puts("You whisper '{r$t{x'", ch, argument, NULL,
		 TO_CHAR | ACT_NODEAF, POS_DEAD);
	act("$n whispers '{r$t{x'", ch, argument, NULL,
	    TO_ROOM | ACT_TOBUF | ACT_NOTWIT | ACT_STRANS | ACT_NODEAF |
	    (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0));

	if (!IS_NPC(ch)) {
		CHAR_DATA *mob, *mob_next;
		for (mob = ch->in_room->people; mob != NULL; mob = mob_next) {
			mob_next = mob->next_in_room;
			if (IS_NPC(mob) && HAS_TRIGGER(mob, TRIG_SPEECH)
			    && mob->position == mob->pIndexData->default_pos)
				mp_act_trigger(argument, mob, ch, NULL, NULL,
					       TRIG_SPEECH);
		}
	}

	for (char_obj = ch->carrying; char_obj != NULL;
	     char_obj = char_obj_next) {
		char_obj_next = char_obj->next_content;
		oprog_call(OPROG_SPEECH, char_obj, ch, argument);
	}

	for (char_obj = ch->in_room->contents; char_obj != NULL;
	     char_obj = char_obj_next) {
		char_obj_next = char_obj->next_content;
		oprog_call(OPROG_SPEECH, char_obj, ch, argument);
	}
}

void do_say(CHAR_DATA * ch, const char *argument)
{
	OBJ_DATA *char_obj;
	OBJ_DATA *char_obj_next;

	if (IS_NPC(ch)
	    && IS_SET(ch->pIndexData->act, ACT_MUTE))
		return;

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

	argument = garble(ch, argument);
	act_puts("You say '{G$t{x'", ch, argument, NULL,
		 TO_CHAR | ACT_NODEAF, POS_DEAD);
	act("$n says '{G$t{x'", ch, argument, NULL,
	    TO_ROOM | ACT_TOBUF | ACT_NOTWIT | ACT_STRANS | ACT_NODEAF |
	    (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0));

	if (!IS_NPC(ch)) {
		CHAR_DATA *mob, *mob_next;
		for (mob = ch->in_room->people; mob != NULL; mob = mob_next) {
			mob_next = mob->next_in_room;
			if (IS_NPC(mob) && HAS_TRIGGER(mob, TRIG_SPEECH)
			    && mob->position == mob->pIndexData->default_pos)
				mp_act_trigger(argument, mob, ch, NULL, NULL,
					       TRIG_SPEECH);
		}
	}

	for (char_obj = ch->carrying; char_obj != NULL;
	     char_obj = char_obj_next) {
		char_obj_next = char_obj->next_content;
		oprog_call(OPROG_SPEECH, char_obj, ch, argument);
	}

	for (char_obj = ch->in_room->contents; char_obj != NULL;
	     char_obj = char_obj_next) {
		char_obj_next = char_obj->next_content;
		oprog_call(OPROG_SPEECH, char_obj, ch, argument);
	}
}


/* this goes into act_comm.c */

void do_tell_raw(CHAR_DATA * ch, CHAR_DATA * victim, const char *msg)
{
	char buf[MAX_STRING_LENGTH];

	if (ch == victim) {
		char_puts("Talking to yourself, eh?\n", ch);
		return;
	}

	if (IS_AFFECTED(ch, AFF_SLEEP) && victim != ch->replyimm) {
		char_puts("In your dreams, or what?\n", ch);
		return;
	}

	if (IS_SET(ch->restricted_channels, CHAN_GLOBAL_TELL)) {
		char_puts("Your message didn't get through.\n", ch);
		return;
	}

	if ((victim == NULL)
	    || (IS_NPC(victim) && victim->in_room != ch->in_room)) {
		char_puts("They aren't here.\n", ch);
		return;
	}
	if ((victim != ch->reply)
	    && is_affected(victim, gsn_vampire)
	    && !(IS_NPC(ch)) && !IS_IMMORTAL(ch)) {
		char_puts("They aren't here.\n", ch);
		return;
	}
	if (IS_SET(victim->comm, (COMM_QUIET | COMM_DEAF))
	    && !IS_IMMORTAL(ch) && !IS_IMMORTAL(victim)) {
		act_puts("$E is not receiving tells.", ch, 0, victim,
			 TO_CHAR, POS_DEAD);
		return;
	}

	if (!IS_IMMORTAL(victim))
		msg = garble(ch, msg);

	/* anonymous color for tells
	 * so morts can keep track of how many wizi immorts
	 * are talking
	 */
	if (IS_IMMORTAL(ch) && !can_see(victim, ch))
		snprintf(buf, sizeof(buf),
			 "{%cYou{x tell $N '{G$t{x'",
			 ch->pcdata->anon_color ? ch->pcdata->anon_color : 'x');
	else
		snprintf(buf, sizeof(buf), "You tell $N '{G$t{x'");

	act_puts(buf, ch, msg, victim, TO_CHAR | ACT_NODEAF, POS_DEAD);

	if (IS_IMMORTAL(ch) && !can_see(victim, ch))
		snprintf(buf, sizeof(buf),
			 "{%c$n{x tells you '{G$t{x'",
			 ch->pcdata->anon_color ? ch->pcdata->anon_color : 'x');
	else
		snprintf(buf, sizeof(buf), "$n tells you '{G$t{x'");

	act_puts(buf,
		 ch, msg, victim,
		 TO_VICT | ACT_TOBUF | ACT_NOTWIT | ACT_NODEAF |
		 (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0),
		 POS_SLEEPING);

	if (IS_NPC(ch))
		return;

	if (IS_NPC(victim)) {
		if (HAS_TRIGGER(victim, TRIG_SPEECH))
			mp_act_trigger(msg, victim, ch, NULL, NULL,
				       TRIG_SPEECH);
	} else {
		if (!IS_IMMORTAL(victim)
		    && !IS_IMMORTAL(ch)
		    && is_name(ch->name, victim->pcdata->twitlist))
			return;

		if (victim->desc == NULL)
			act_puts("$N seems to have misplaced $S link but "
				 "your tell will go through if $E returns.",
				 ch, NULL, victim, TO_CHAR, POS_DEAD);
		else if (IS_SET(victim->comm, COMM_AFK))
			act_puts("$E is AFK, but your tell will go through "
				 "when $E returns.",
				 ch, NULL, victim, TO_CHAR, POS_DEAD);
		victim->reply = ch;

		if (IS_IMMORTAL(ch))
			victim->replyimm = ch;
	}
}

void do_tell(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];

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

	do_tell_raw(ch, get_char_world(ch, arg), argument);
}

void do_reply(CHAR_DATA * ch, const char *argument)
{
	do_tell_raw(ch, ch->reply, argument);
}

void do_replyimm(CHAR_DATA * ch, const char *argument)
{
	do_tell_raw(ch, ch->replyimm, argument);
}

void do_noreply(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim = NULL;

	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0' || arg[0] == '\0') {
		char_puts("Refuse replies from whom?\n", ch);
		return;
	}

	if (!str_cmp(arg, "all")) {
		for (victim = char_list; victim && !IS_NPC(victim);
		     victim = victim->next)
			if (victim != ch && !IS_IMMORTAL(victim)) {
				if (victim->reply == ch)
					victim->reply = NULL;
				if (victim->replyimm == ch)
					victim->replyimm = NULL;
			}
	} else {
		victim = get_char_world(ch, arg);
		if (victim != NULL && !IS_IMMORTAL(victim)) {
			if (victim != NULL && victim->reply == ch)
				victim->reply = NULL;
			if (victim != NULL && victim->replyimm == ch)
				victim->replyimm = NULL;
		}
	}
	char_puts("They will no longer reply to you.\n", ch);
}

void do_gtell(CHAR_DATA * ch, const char *argument)
{
	CHAR_DATA *gch;
	int i;
	int flags;

	if (IS_AFFECTED(ch, AFF_SLEEP)) {
		char_puts("In your dreams, or what?\n", ch);
		return;
	}

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

	if (IS_SET(ch->restricted_channels, CHAN_GLOBAL_TELL)) {
		char_puts("Your message didn't get through!\n", ch);
		return;
	}

	argument = garble(ch, argument);
	flags = TO_VICT | ACT_TOBUF | ACT_STRANS |
	    (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0);
	for (i = 0, gch = char_list; gch; gch = gch->next) {
		if (IS_NPC(gch))
			break;

		i++;
		if (is_same_group(gch, ch) && !is_affected(gch, gsn_deafen))
			act_puts("$n tells the group '{G$t{x'",
				 ch, argument, gch, flags, POS_DEAD);
	}

	if (i > 1 && !is_affected(ch, gsn_deafen))
		act_puts("You tell your group '{G$t{x'",
			 ch, argument, NULL, TO_CHAR, POS_DEAD);
	else
		char_puts("Quit talking to yourself. You are all alone.\n", ch);
}

void do_emote(CHAR_DATA * ch, const char *argument)
{
	if (!IS_NPC(ch) && IS_SET(ch->restricted_channels, CHAN_GLOBAL_EMOTE)) {
		char_puts("You can't show your emotions.\n", ch);
		return;
	}

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

	argument = garble(ch, argument);
	act("$n $T", ch, NULL, argument,
	    TO_ROOM | ACT_TOBUF | ACT_NOTRIG | ACT_NOTWIT |
	    (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0));
	act("$n $T", ch, NULL, argument, TO_CHAR | ACT_NOTRIG);
}

void do_pmote(CHAR_DATA * ch, const char *argument)
{
	CHAR_DATA *vch;
	const char *letter, *name;
	char last[MAX_INPUT_LENGTH], temp[MAX_STRING_LENGTH];
	int matches = 0;
	int flags;

	if (!IS_NPC(ch) && IS_SET(ch->restricted_channels, CHAN_GLOBAL_EMOTE)) {
		char_puts("You can't show your emotions.\n", ch);
		return;
	}

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

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

	flags = TO_CHAR | ACT_TOBUF | ACT_NOTWIT | ACT_NOTRIG |
	    (IS_NPC(ch) && !IS_AFFECTED(ch, AFF_CHARM) ? ACT_TRANS : 0);

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

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

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

		for (; *letter != '\0'; letter++) {
			if (*letter == '\'' && matches == strlen(vch->name)) {
				strnzcat(temp, sizeof(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)) {
					strnzcat(temp, sizeof(temp), "you");
					last[0] = '\0';
					name = vch->name;
					continue;
				}
				strnzncat(last, sizeof(last), letter, 1);
				continue;
			}

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

		act("$N $t", vch, temp, ch, flags);
	}
}

void do_dmtalk(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_DM_WIZ);
}

void do_immtalk(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_WIZ);
}

void do_wizhelp(CHAR_DATA * ch, const char *argument)
{
	if (argument[0] == '\0') {
		act_puts("Y{Gou must describe the problem you"
			 " wish the immortal staff to address.{x",
			 ch, argument, NULL, TO_CHAR, POS_DEAD);
		return;
	}
	if (IS_IMMORTAL(ch)) {
		char_puts("This channel is reserved for mortals"
			  " asking for help.\n", ch);
		return;
	}

	public_channel(ch, argument, CHAN_WIZHELP);
}

void do_yell(CHAR_DATA * ch, const char *argument)
{
	if (argument[0] == '\0') {
		char_puts("Yell what?\n", ch);
		return;
	}

	if (ch->position < POS_RESTING)
		return;

	public_channel(ch, argument, CHAN_YELL);
}

void do_questtalk(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_QUEST);
}

void do_advice(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_ADVICE);
}

void do_shout(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_SHOUT);
}

void do_music(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_MUSIC);
}

void do_gossip(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_GOSSIP);
}

void do_death_announce(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_DEATH);
}

void do_offer(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_TRADE);
}

void do_ooc(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_OOC);
}

void do_newbie(CHAR_DATA * ch, const char *argument)
{
	public_channel(ch, argument, CHAN_NEWBIE);
}

void do_clan(CHAR_DATA * ch, const char *argument)
{
	if (!ch->clan) {
		char_puts("You are not in a clan.\n", ch);
		return;
	}
	if (clan_lookup(ch->clan) == NULL) {
		char_puts("Your clan is closed.\n", ch);
		return;
	}
	public_channel(ch, argument, CHAN_CLAN);
}

void do_pray(CHAR_DATA * ch, const char *argument)
{
	if (ch->religion == 0) {
		char_puts("You must first choose a god to worship.\n", ch);
		return;
	}

	public_channel(ch, argument, CHAN_PRAY);
}

void do_pose(CHAR_DATA * ch, const char *argument)
{
	class_t *cl;
	pose_t *pose;
	int maxnum;

	if (IS_NPC(ch)
	    || (cl = class_lookup(ch->class)) == NULL || cl->poses.nused == 0)
		return;

	maxnum = UMIN(ch->level, cl->poses.nused - 1);
	pose = VARR_GET(&cl->poses, number_range(0, maxnum));
	act(pose->self, ch, NULL, NULL, TO_CHAR);
	act(pose->others, ch, NULL, NULL, TO_ROOM | ACT_TOBUF);
}

void do_bug(CHAR_DATA * ch, const char *argument)
{
	char buf[MAX_STRING_LENGTH];

	snprintf(buf, sizeof(buf), "%s [%5d] %s: %s",
		 compact_date_str(current_time),
		 ch->in_room ? ch->in_room->vnum : 0, ch->name, argument);

	if (!append_file(BUG_FILE, buf)) {
		char_puts
		    ("error opening file, please report your bug to the immortals",
		     ch);
		BUG("Couldn't open %s", BUG_FILE);
	} else
		char_puts("Bug logged.\n", ch);

	snprintf(buf, sizeof(buf),
		 "{w[{YBUG REPORT{w]: {D[{W%5d{D] {w%s{Y:{x %s{x",
		 ch->in_room ? ch->in_room->vnum : 0, ch->name, argument);

	wiznet(buf, NULL, NULL, WIZ_ANNOUNCE, 0, 0);

	return;
}

void do_typo(CHAR_DATA * ch, const char *argument)
{
	char buf[MAX_STRING_LENGTH];

	snprintf(buf, sizeof(buf), "%s [%5d] %s: %s",
		 compact_date_str(current_time),
		 ch->in_room ? ch->in_room->vnum : 0, ch->name, argument);

	if (!append_file(TYPO_FILE, buf)) {
		char_puts
		    ("error opening file, please report your typo to the immortals",
		     ch);
		BUG("Couldn't open %s", TYPO_FILE);
	} else
		char_puts("Typo logged.\n", ch);

	snprintf(buf, sizeof(buf),
		 "{w[{YTYPO REPORT{w]: {D[{W%5d{D] {w%s{Y:{x %s{x",
		 ch->in_room ? ch->in_room->vnum : 0, ch->name, argument);

	wiznet(buf, NULL, NULL, WIZ_ANNOUNCE, 0, 0);

	return;
}

void do_rent(CHAR_DATA * ch, const char *argument)
{
	char_puts("There is no rent here.  Just save and quit.\n", ch);
	return;
}

void do_qui(CHAR_DATA * ch, const char *argument)
{
	char_puts("If you want to QUIT, you have to spell it out.\n", ch);
}

void do_quit(CHAR_DATA * ch, const char *argument)
{
	if (ch->pnote) {
		act_puts("You have an unfinished $t in progress.",
			 ch, flag_string(note_types, ch->pnote->type), NULL,
			 TO_CHAR, POS_DEAD);
		return;
	}

	quit_char(ch, 0);
}

void quit_char(CHAR_DATA * ch, int flags)
{
	DESCRIPTOR_DATA *d, *d_next;
	CHAR_DATA *vch, *vch_next;
	OBJ_DATA *obj, *obj_next, *obj_in;
	AFFECT_DATA *pAf = NULL;
	char buf[MAX_STRING_LENGTH];
	int cn;
	const char *name;
	int limiteds = 0;

	if (IS_NPC(ch))
		return;

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

	if (ch->position < POS_STUNNED) {
		char_puts("You're not DEAD yet.\n", ch);
		return;
	}

	if (IS_AFFECTED(ch, AFF_CHARM)) {
		char_puts("You don't want to leave your master.\n", ch);
		return;
	}

/*	if (IS_SET(ch->state_flags, STATE_NOEXP)) {
		char_puts("You don't want to lose your spirit.\n", ch);
		return;
	}
*/

	if (IS_AFFECTED(ch, AFF_SLEEP)) {
		char_puts("You cannot quit, you are in deep sleep.\n", ch);
		return;
	}

	if (ch->position == POS_SLEEPING) {
		char_puts("In your dreams, or what?\n", ch);
		return;
	}

	if (auction.item != NULL
	    && ((ch == auction.buyer) || (ch == auction.seller))) {
		char_puts("Wait till you have sold/bought the item "
			  "on auction.\n", ch);
		return;
	}

	if (!IS_IMMORTAL(ch)) {
		if (IS_PUMPED(ch)) {
			char_puts("Your adrenaline is gushing!"
				  " You can't quit yet.\n", ch);
			return;
		}

		if (is_affected(ch, gsn_witch_curse)) {
			char_puts("You are cursed. Wait till you DIE!\n", ch);
			return;
		}

		if (ch->in_room->area->clan
		    && ch->in_room->area->clan != ch->clan) {
			char_puts("You can't quit here.\n", ch);
			return;
		}

		if (ch->in_room && IS_RAFFECTED(ch->in_room, RAFF_ESPIRIT)) {
			char_puts
			    ("Evil spirits in the area prevents you from leaving.\n",
			     ch);
			return;
		}

		if (!get_skill(ch, gsn_evil_spirit)
		    && is_affected(ch, gsn_evil_spirit)) {
			char_puts
			    ("Evil spirits in you prevents you from leaving.\n",
			     ch);
			return;
		}

		/* count the number of limiteds the person has */
		for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
			if (IS_OBJ_LIMITED(obj->pIndexData))
				limiteds++;

		if (limiteds > allowed_limiteds(ch->pcdata->pdata)) {
			char_puts("You have too many artifacts to"
				  " journey into the astral plane.\n", ch);
			return;
		}

	}

	if (IS_SET(ch->state_flags, STATE_GHOST)) {
		char_puts("You return to your normal form.\n", ch);
		REMOVE_BIT(ch->state_flags, STATE_GHOST);
		REMOVE_BIT(ch->affected_by, AFF_FLYING);
		REMOVE_BIT(ch->affected_by, AFF_PASS_DOOR);
	}

	/* sync up the persistant data before logging off */
	ch_update_usage(ch->pcdata->pdata, TRUE);
	sync_ch_pdata(ch);
	ch->pcdata->pdata->online = FALSE;

	char_puts("You pass into the astral plane, and feel your powers sucked away.\n",
	     ch);
	act_puts("$n has passed into the astral plane.", ch, NULL, NULL, TO_ROOM, POS_RESTING);
	log_printf("%s has quit.", ch->name);

	snprintf(buf, sizeof(buf),
		 "logout: {D$N{x - %s",
		 (ch->desc) 
		 ? ((ch->pcdata->fake_ip) ? ch->pcdata->fake_ip
		 			 : ch->desc->host)
		 : "<no descriptor>");
	wiznet(buf, ch, NULL, WIZ_LOGINS, 0, ch->level);

	/*
	 * remove quest objs for this char, drop quest objs for other chars
	 */
	for (obj = object_list; obj != NULL; obj = obj_next) {
		obj_next = obj->next;

		if (obj->pIndexData->vnum >= QUEST_OBJ_FIRST
		    && obj->pIndexData->vnum <= QUEST_OBJ_LAST) {
			if (obj->ed == NULL ||
			    strstr(mlstr_mval(obj->ed->description),
				   ch->name) != NULL)
				extract_obj(obj, 0);
			else if (obj->carried_by == ch) {
				obj_from_char(obj);
				obj_to_room(obj, ch->in_room);
			}
		}

		if (IS_SET(obj->pIndexData->extra_flags, ITEM_CLAN)
		    || IS_SET(obj->pIndexData->extra_flags, ITEM_QUIT_DROP)
		    || (IS_OBJ_LIMITED(obj->pIndexData)
			&& (obj->pIndexData->level > get_wear_level(ch, obj)))) {

			/* in a room */
			if (obj->in_room != NULL)
				continue;

			/* if in container (obj_in) */
			if ((obj_in = obj->in_obj) != NULL) {
				for (; obj_in->in_obj != NULL;
				     obj_in = obj_in->in_obj);
				if (obj_in->carried_by != ch)
					continue;
			};

			/* carried by this character */
			if (obj->carried_by == ch)
				obj_from_char(obj);

			/* carried by someone else */
			else if (obj->carried_by != NULL)
				continue;

			/* don't extract the auction item */
			if (auction.item == obj)
				continue;

			/* extract from the container (obj_in) */
			if (obj_in != NULL)
				obj_from_obj(obj);

			/* extract all limited objects that are not in the
			 * desired range*/
			if (IS_OBJ_LIMITED(obj->pIndexData)
			    && (IS_NEWBIE(ch)
				|| obj->pIndexData->level > get_wear_level(ch,
									   obj)))
			{

				act_puts
				    ("You're are unable to clutch the {mpower{x of"
				     " $p in the astral plane, so it {Wva{wnis{Dhes{x.",
				     ch, obj, NULL, TO_CHAR, POS_DEAD);

				DEBUG(DEBUG_RECLAIM,
				    "%s[%d] reclaim limited [%d-%d/%d] [%d] %s",
				    ch->name, ch->level,
				    obj->pIndexData->level,
				    obj->pIndexData->count,
				    obj->pIndexData->limit,
				    obj->pIndexData->vnum,
				    obj->pIndexData->name);

				extract_obj(obj, 0);
				continue;
			}

			/* drop quit-drop items */
			else if (IS_SET
				 (obj->pIndexData->extra_flags,
				  ITEM_QUIT_DROP)) {
				if (ch->in_room != NULL)
					obj_to_room(obj, ch->in_room);
				else
					extract_obj(obj, 0);
			}

			/* return clan items to their altars */
			else
				for (cn = 0; cn < clans.nused; cn++)
					if (obj == clan_lookup(cn)->obj_ptr)
						obj_to_room(obj,
							    get_room_index
							    (clan_lookup(cn)->
							     altar_vnum));
		}
	}

	for (vch = char_list; vch; vch = vch_next) {
		vch_next = vch->next;
		if (is_affected(vch, gsn_doppelganger)
		    && vch->doppel == ch) {
			char_puts
			    ("You shift to your true form as your victim leaves.\n",
			     vch);
			affect_strip(vch, gsn_doppelganger);
		}

		/* if you log out you remove the curse from your victim */
		if (is_affected(vch, gsn_witch_curse)
		    && vch->cursed_by_witch == ch) {
			pAf = affect_find(vch->affected, gsn_witch_curse);
			affect_remove(vch, pAf);
			act("You feel $N's {Dcurse{x is lifted!",
			    vch, NULL, ch, TO_CHAR);
			act("As you depart the realm so does the power of your curse on $N.", ch, NULL, vch, TO_CHAR);

		}

		if (vch->guarding == ch) {
			act("You stop guarding $N.", vch, NULL, ch, TO_CHAR);
			act("$n stops guarding you.", vch, NULL, ch, TO_VICT);
			act("$n stops guarding $N.", vch, NULL, ch, TO_NOTVICT);
			vch->guarding = NULL;
			ch->guarded_by = NULL;
		}

		if (vch->guarded_by == ch) {
			act("You stop guarding $N.", ch, NULL, vch, TO_CHAR);
			act("$n stops guarding you.", ch, NULL, vch, TO_VICT);
			act("$n stops guarding $N.", ch, NULL, vch, TO_NOTVICT);
			vch->guarded_by = NULL;
			ch->guarding = NULL;
		}

		if (vch->last_fought == ch) {
			vch->last_fought = NULL;
			back_home(vch);
		}

		if (vch->hunting == ch)
			vch->hunting = NULL;

		if (vch->hunter == ch)
			vch->hunter = NULL;

		if (vch->target == ch) {
			if (IS_NPC(vch)
			    && vch->pIndexData->vnum == MOB_VNUM_SHADOW) {
				act("$n slowly fades away.",
				    vch, NULL, NULL, TO_ROOM);
				extract_char(vch, 0);
				continue;
			}
			if (IS_NPC(vch)
			    && vch->pIndexData->vnum == MOB_VNUM_STALKER) {
				if (ch->pcdata->saved_stalkers++ > 10)
					ch->pcdata->saved_stalkers = 10;
				doprintf(do_clan, vch,
					 "%s has left the realm,"
					 " I will retire for now.",
					 PERS(ch, vch));
				act("$n slowly fades away.", vch, NULL, NULL,
				    TO_ROOM);
				extract_char(vch, 0);
			}
		}
	}

	if (ch->guarded_by != NULL) {
		ch->guarded_by->guarding = NULL;
		ch->guarded_by = NULL;
	}

	/*
	 * After extract_char the ch is no longer valid!
	 */
	save_char_obj(ch, FALSE);
	name = str_qdup(ch->name);
	d = ch->desc;
	extract_char(ch, flags);

	if (d)
		close_descriptor(d);

	/*
	 * toast evil cheating bastards 
	 *
	 * Workaround against clone cheat --
	 * Log in once, connect a second time and enter only name,
	 * drop all and quit with first character, finish login
	 * with second. This clones the player's inventory.
	 */
	for (d = descriptor_list; d; d = d_next) {
		CHAR_DATA *tch;

		d_next = d->next;
		tch = d->original ? d->original : d->character;
		if (tch && !str_cmp(name, tch->name)) {
			if (d->connected == CON_PLAYING)
				extract_char(tch, 0);
			close_descriptor(d);
		}
	}
}

void do_save(CHAR_DATA * ch, const char *argument)
{
	if (IS_NPC(ch))
		return;

	if (ch->level < 2) {
		char_puts("You must be at least level 2 for saving.\n", ch);
		return;
	}
	save_char_obj(ch, FALSE);
	WAIT_STATE(ch, PULSE_VIOLENCE);
}

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

	one_argument(argument, arg, sizeof(arg));

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

	if ((victim = get_char_room(ch, arg)) == NULL) {
		char_puts("They aren't here.\n", ch);
		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) {
			char_puts("You already follow yourself.\n", ch);
			return;
		}
		stop_follower(ch);
		return;
	}

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

	REMOVE_BIT(ch->conf_flags, PLR_CONF_NOFOLLOW);

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

	add_follower(ch, victim);
}

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

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

	if (can_see(master, ch))
		act_puts("$n now follows you.", ch, NULL, master,
			 TO_VICT, POS_RESTING);
	act_puts("You now follow $N.", ch, NULL, master, TO_CHAR, POS_RESTING);
}

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_bit_strip(ch, TO_AFFECTS, AFF_CHARM);
	}

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

	if (ch->master->pet == ch)
		ch->master->pet = NULL;

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

/* removes all your pets from your group */
void disband_pets(CHAR_DATA * ch)
{
	CHAR_DATA *gch;
	CHAR_DATA *next;
	AFFECT_DATA af;

	gch = next = npc_list;

	while (next) {
		gch = next;
		next = gch->next;
		if (IS_AFFECTED(gch, AFF_CHARM)
		    && gch->master == ch) {
			stop_follower(gch);
			/* XXX - this doesn't work */
			if (gch->position == POS_FIGHTING) {
				af.where = TO_AFFECTS;
				af.type = gsn_fear;
				af.level = ch->level + 10;
				af.duration = ch->level / 10;
				af.location = 0;
				af.modifier = 0;
				af.bitvector = AFF_FEAR;
				affect_to_char(gch, &af);
			}
		}
	}
}

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

	gch = next = npc_list;

	while (next) {
		gch = next;
		next = gch->next;
		if (IS_AFFECTED(gch, AFF_CHARM)
		    && gch->master == ch) {
			if (gch->in_room)
				act("$n slowly fades away.", gch, NULL, NULL,
				    TO_ROOM);
			extract_char(gch, 0);
		}
	}
	ch->pet = NULL;
}

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

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

	ch->leader = NULL;

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


void do_order(CHAR_DATA * ch, const char *argument)
{
	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, sizeof(arg));
	one_argument(argument, arg2, sizeof(arg2));

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

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

	if (is_affected(ch, gsn_rnet_trap)) {
		char_puts
		    ("There's not enough room to effectively issue orders.\n",
		     ch);
		return;
	}


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

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

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

	found = FALSE;
	for (och = ch->in_room->people; 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;
			act("$n orders you to '$t'.",
			    ch, argument, och, TO_VICT);
			if (!interpret_raw(och, argument, TRUE, 0)) {
				act("But you think better of it.",
				    ch, argument, och, TO_VICT);
				act("$N can't seem to be persuaded to do that.",
				    ch, argument, och, TO_CHAR);
			}
		}
	}

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

CHAR_DATA *leader_lookup(CHAR_DATA * ch)
{
	CHAR_DATA *res;
	for (res = ch; res->leader != NULL; res = res->leader);
	return res;
}

void do_group(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	char format[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	CHAR_DATA *gch;
	int ansi_len = 0;

	one_argument(argument, arg, sizeof(arg));

	if (arg[0] == '\0') {

		act_puts("$N's group:", ch, NULL, leader_lookup(ch),
			 TO_CHAR, POS_DEAD);

		for (gch = char_list; gch; gch = gch->next) {
			if (is_same_group(gch, ch)) {
				ansi_len =
				    astrlen(fix_short(PERS(gch, ch)), 30);

				snprintf(format, sizeof(format),
					 "[%%1s] %%-%ds"
					 " %%3d%%%%hp"
					 " %%3d%%%%mana"
					 " %%3d%%%%move "
					 "   %%7d tnl\n", 30 + ansi_len);

				char_printf(ch,
					    format,
					    (gch->level > ch->level) ? "+" :
					    (gch->level < ch->level) ? "-" :
					    "=",
					    fix_short(PERS(gch, ch)),
					    (100 * gch->hit) / UMAX(1,
								    gch->
								    max_hit),
					    (100 * gch->mana) / UMAX(1,
								     gch->
								     max_mana),
					    (100 * gch->move) / UMAX(1,
								     gch->
								     max_move),
					    IS_NPC(gch)
					    || ch->level >=
					    LEVEL_HERO ? 0 : exp_to_level(gch));
			}

		}
		return;
	}

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

	if (victim == ch) {
		char_puts("Huh? Grouping with yourself?!\n", ch);
		return;
	}

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

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

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

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


	if (is_same_group(victim, ch) && ch != victim) {
		if (ch->guarding == victim || victim->guarded_by == ch) {
			act("You stop guarding $N.", ch, NULL, victim, TO_CHAR);
			act("$n stops guarding you.",
			    ch, NULL, victim, TO_VICT);
			act("$n stops guarding $N.",
			    ch, NULL, victim, TO_NOTVICT);
			victim->guarded_by = NULL;
			ch->guarding = NULL;
		}

		victim->leader = NULL;
		act_puts("$n removes $N from $s group.", ch, NULL, victim,
			 TO_NOTVICT, POS_SLEEPING);
		act_puts("$n removes you from $s group.", ch, NULL, victim,
			 TO_VICT, POS_SLEEPING);
		act_puts("You remove $N from your group.", ch, NULL, victim,
			 TO_CHAR, POS_SLEEPING);

		if (victim->guarded_by != NULL
		    && !is_same_group(victim, victim->guarded_by)) {
			act("You stop guarding $N.",
			    victim->guarded_by, NULL, victim, TO_CHAR);
			act("$n stops guarding you.",
			    victim->guarded_by, NULL, victim, TO_VICT);
			act("$n stops guarding $N.",
			    victim->guarded_by, NULL, victim, TO_NOTVICT);
			victim->guarded_by->guarding = NULL;
			victim->guarded_by = NULL;
		}
		return;
	}

	if (ch->level - victim->level < -8 || ch->level - victim->level > 8) {
		act_puts("$N cannot join $n's group.", ch, NULL, victim,
			 TO_NOTVICT, POS_SLEEPING);
		act_puts("You cannot join $n's group.", ch, NULL, victim,
			 TO_VICT, POS_SLEEPING);
		act_puts("$N cannot join your group.", ch, NULL, victim,
			 TO_CHAR, POS_SLEEPING);
		return;
	}

	if (is_affected(ch, gsn_dishonor)) {
		act_puts("Regaining your honor is something you must do alone.",
			 victim, NULL, NULL, TO_CHAR, POS_SLEEPING);
		return;
	}

	if (is_affected(victim, gsn_dishonor)) {
		act_puts("You refuse to group with a fallen samurai.",
			 ch, NULL, NULL, TO_CHAR, POS_SLEEPING);
		act_puts("Your dishonorable conduct makes grouping impossible.",
			 victim, NULL, NULL, TO_CHAR, POS_SLEEPING);
		return;
	}

	for (gch = char_list; gch; gch = gch->next) {
		if (is_same_group(gch, ch)
		    && !IS_NPC(gch)
		    && !(victim->clan > 0 && victim->clan == gch->clan)) {
			if (IS_GOOD(victim) && IS_EVIL(gch)) {
				act_puts
				    ("Allying yourself with one as pure as $n disgusts you.",
				     victim, NULL, gch, TO_VICT, POS_SLEEPING);
				act_puts
				    ("Allying yourself with on as vile as $N disgusts you.",
				     victim, NULL, gch, TO_CHAR, POS_SLEEPING);

				if (ch != gch) {
					act_puts
					    ("Doesn't look like $N will ally with such a vile soul in the group.",
					     ch, NULL, victim, TO_CHAR,
					     POS_SLEEPING);
				}
				return;
			}

			if (IS_GOOD(gch) && IS_EVIL(victim)) {
				act_puts
				    ("Allying yourself with one as vile as $N disgusts you.",
				     gch, NULL, victim, TO_CHAR, POS_SLEEPING);
				act_puts
				    ("Allying yourself with one as pure as $n disgusts you.",
				     gch, NULL, victim, TO_VICT, POS_SLEEPING);
				if (ch != gch) {
					act_puts
					    ("Doesn't look like $N will ally with such a pure soul in the group.",
					     ch, NULL, victim, TO_CHAR,
					     POS_SLEEPING);
				}
				return;
			}

			if ((HAS_SKILL(victim, gsn_ruler_badge)
			     && HAS_SKILL(gch, gsn_disperse))
			    || (HAS_SKILL(victim, gsn_disperse)
				&& HAS_SKILL(gch, gsn_ruler_badge))
			    || (HAS_SKILL(victim, gsn_evil_spirit)
				&& HAS_SKILL(gch, gsn_riding))
			    || (HAS_SKILL(victim, gsn_riding)
				&& HAS_SKILL(gch, gsn_evil_spirit))
			    || (HAS_SKILL(victim, gsn_mastering_spell)
				&& HAS_SKILL(gch, gsn_spellbane))
			    || (HAS_SKILL(victim, gsn_spellbane)
				&& HAS_SKILL(gch, gsn_mastering_spell))) {
				act_puts
				    ("You hate $n's clan, how can you join $n's group?",
				     victim, NULL, gch, TO_VICT, POS_SLEEPING);
				act_puts
				    ("You hate $N's clan, how can you want $N to join your group",
				     victim, NULL, gch, TO_CHAR, POS_SLEEPING);
				if (ch != gch) {
					act_puts
					    ("Looks like $N has something against someone in your group.",
					     ch, NULL, victim, TO_CHAR,
					     POS_SLEEPING);
				}
				return;
			}
		}
	}
	victim->leader = ch;
	act_puts("$N joins $n's group.", ch, NULL, victim, TO_NOTVICT,
		 POS_SLEEPING);
	act_puts("You join $n's group.", ch, NULL, victim, TO_VICT,
		 POS_SLEEPING);
	act_puts("$N joins your group.", ch, NULL, victim, TO_CHAR,
		 POS_SLEEPING);
}


/*
 * 'Split' originally by Gnort, God of Chaos.
 */
void do_split(CHAR_DATA * ch, const char *argument)
{
	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, sizeof(arg1));
	one_argument(argument, arg2, sizeof(arg2));

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

	amount_silver = atoi(arg1);

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

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

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

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

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

	if (members < 2) {
		char_puts("Just keep it all.\n", ch);
		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) {
		char_puts("Don't even bother, cheapskate.\n", ch);
		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)
		char_printf(ch,
			    "You split %d silver coins. Your share is %d silver.\n",
			    amount_silver, share_silver + extra_silver);

	if (share_gold > 0)
		char_printf(ch,
			    "You split %d gold coins. Your share is %d gold.\n",
			    amount_gold, share_gold + extra_gold);

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

	for (gch = ch->in_room->people; 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;
}

void do_speak(CHAR_DATA * ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	int language;
	race_t *r;

	if (IS_NPC(ch)
	    || (r = race_lookup(ch->pcdata->race)) == NULL || !r->pcdata)
		return;

	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		char_printf(ch, "You now speak %s.\n",
			    flag_string(slang_table, ch->slang));
		char_puts("You can speak :\n", ch);
		char_printf(ch, "       common, %s\n",
			    flag_string(slang_table, r->pcdata->slang));
		return;
	}

	if ((language = flag_value(slang_table, arg)) < 0
	    || language >= SLANG_MAX) {
		char_puts("You never heard of that language.\n", ch);
		return;
	}

	if (IS_NPC(ch) || IS_IMMORTAL(ch))
		ch->slang = language;
	else if (language != SLANG_COMMON && language != r->pcdata->slang) {
		char_puts("But you don't speak that language!\n", ch);
		return;
	} else
		ch->slang = language;

	char_printf(ch, "Now you speak %s.\n",
		    flag_string(slang_table, ch->slang));
}

DO_FUN(do_twit)
{
	char arg[MAX_STRING_LENGTH];

	if (IS_NPC(ch)) {
		char_puts("Huh?\n", ch);
		return;
	}

	one_argument(argument, arg, sizeof(arg));

	if (arg[0] == '\0') {
		char_printf(ch, "Current twitlist is [%s]\n",
			    ch->pcdata->twitlist);
		return;
	}

	name_toggle(&ch->pcdata->twitlist, arg, ch, "Twitlist");
}

DO_FUN(do_lang)
{
	char arg[MAX_STRING_LENGTH];
	int lang;
	lang_t *l;

	if (langs.nused == 0) {
		char_puts("No languages defined.\n", ch);
		return;
	}

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

	if (*arg == '\0') {
		l = varr_get(&langs, ch->lang);
		if (l == NULL) {
			log_printf("do_lang: %s: lang == %d\n",
				   ch->name, ch->lang);
			l = VARR_GET(&langs, ch->lang = 0);
		}
		char_printf(ch, "Interface language is '%s'.\n", l->name);
		return;
	}

	lang = lang_lookup(arg);
	if (lang < 0) {
		char_puts("Usage: lang [ ", ch);
		for (lang = 0; lang < langs.nused; lang++) {
			l = VARR_GET(&langs, lang);
			if (IS_SET(l->flags, LANG_HIDDEN))
				continue;
			char_printf(ch, "%s%s",
				    lang == 0 ? str_empty : " | ", l->name);
		}
		char_puts(" ]\n", ch);
		return;
	}

	ch->lang = lang;
	do_lang(ch, str_empty);
	do_look(ch, str_empty);
}

DO_FUN(do_judge)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;

	if (!HAS_SKILL(ch, gsn_ruler_badge)) {
		char_puts("Huh?\n", ch);
		return;
	}

	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		char_puts("Judge whom?\n", ch);
		return;
	}

	if ((victim = get_char_world(ch, arg)) == NULL) {
		char_puts("They aren't here.\n", ch);
		return;
	}

	if (IS_NPC(victim)) {
		char_puts("Not a mobile, of course.\n", ch);
		return;
	}

	if (IS_IMMORTAL(victim) && !IS_IMMORTAL(ch)) {
		char_puts("You do not have the power to judge Immortals.\n",
			  ch);
		return;
	}

	char_printf(ch, "%s is %s-%s.\n",
		    PERS(victim, ch),
		    flag_string(ethos_table, victim->ethos),
		    flag_string(align_names, NALIGN(victim)));
}

DO_FUN(do_trust)
{
	char arg[MAX_INPUT_LENGTH];

	if (IS_NPC(ch)) {
		char_puts("Huh?\n", ch);
		return;
	}

	one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		if (!ch->pcdata->trust) {
			char_puts
			    ("You do not allow anyone to cast questionable spells on you.\n",
			     ch);
			return;
		}

		if (IS_SET(ch->pcdata->trust, TRUST_ALL)) {
			char_puts
			    ("You allow everyone to cast questionable spells on you.\n",
			     ch);
			return;
		}
		if (IS_SET(ch->pcdata->trust, TRUST_CLAN))
			char_puts
			    ("You trust your clan with questionable spells.\n",
			     ch);
		if (IS_SET(ch->pcdata->trust, TRUST_GROUP))
			char_puts
			    ("You trust your group with questionable spells.\n",
			     ch);
		if (IS_SET(ch->pcdata->trust, TRUST_GOOD))
			char_puts
			    ("You trust angelic souls with questionable spells.\n",
			     ch);
		if (IS_SET(ch->pcdata->trust, TRUST_NEUTRAL))
			char_puts
			    ("You trust balanced souls with questionable spells.\n",
			     ch);
		if (IS_SET(ch->pcdata->trust, TRUST_EVIL))
			char_puts
			    ("You trust demonic souls with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "clan")) {
		if (ch->clan == 0) {
			char_puts("You are not in clan.\n", ch);
			return;
		};

		TOGGLE_BIT(ch->pcdata->trust, TRUST_CLAN);
		if (IS_SET(ch->pcdata->trust, TRUST_CLAN)) {
			REMOVE_BIT(ch->pcdata->trust, TRUST_ALL);
			char_puts
			    ("You now trust your clan with questionable spells.\n",
			     ch);
		} else
			char_puts
			    ("You no longer trust your clan with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "group")) {
		TOGGLE_BIT(ch->pcdata->trust, TRUST_GROUP);
		if (IS_SET(ch->pcdata->trust, TRUST_GROUP)) {
			REMOVE_BIT(ch->pcdata->trust, TRUST_ALL);
			char_puts
			    ("You allow your group to cast questionable spells on you.\n",
			     ch);
		} else
			char_puts
			    ("You no longer trust your group with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "good")) {
		TOGGLE_BIT(ch->pcdata->trust, TRUST_GOOD);
		if (IS_SET(ch->pcdata->trust, TRUST_GOOD)) {
			REMOVE_BIT(ch->pcdata->trust, TRUST_ALL);
			char_puts
			    ("You allow your angelic souls to cast questionable spells on you.\n",
			     ch);
		} else
			char_puts
			    ("You no longer trust angelic souls with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "neutral")) {
		TOGGLE_BIT(ch->pcdata->trust, TRUST_NEUTRAL);
		if (IS_SET(ch->pcdata->trust, TRUST_NEUTRAL)) {
			REMOVE_BIT(ch->pcdata->trust, TRUST_ALL);
			char_puts
			    ("You allow your balanced souls to cast questionable spells on you.\n",
			     ch);
		} else
			char_puts
			    ("You no longer trust balanced souls with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "evil")) {
		TOGGLE_BIT(ch->pcdata->trust, TRUST_EVIL);
		if (IS_SET(ch->pcdata->trust, TRUST_EVIL)) {
			REMOVE_BIT(ch->pcdata->trust, TRUST_ALL);
			char_puts
			    ("You allow your demonic souls to cast questionable spells on you.\n",
			     ch);
		} else
			char_puts
			    ("You no longer trust demonic souls with questionable spells.\n",
			     ch);
		return;
	}

	if (!str_cmp(arg, "all")) {
		ch->pcdata->trust = TRUST_ALL;
		char_puts
		    ("You allow everyone to cast questionable spells on you.\n",
		     ch);
		return;
	}

	if (!str_cmp(arg, "none")) {
		ch->pcdata->trust = 0;
		char_puts
		    ("You do not allow anyone to cast questionable spells on you.\n",
		     ch);
		return;
	}

	char_puts
	    ("Syntax: trust {{ group | clan | good | neutral | evil | all | none }\n",
	     ch);
}

DO_FUN(do_manacle)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	AFFECT_DATA af;
	int sn_manacle;
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;


	if ((sn_manacle = sn_lookup("manacle")) < 0
	    || get_skill(ch, sn_manacle) == 0) {
		char_puts("Huh?\n", ch);
		return;
	}


	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		char_puts("Toggle manacle whom?\n", ch);
		return;
	}

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


	if (IS_IMMORTAL(victim) && ch->level < victim->level) {
		act("Immortals cannot be criminals.",
		    ch, NULL, victim, TO_CHAR);
		return;
	}

	if (victim == ch) {
		char_puts("You cannot do that to yourself.\n", ch);
		return;
	}

	if (IS_SET(victim->state_flags, STATE_WANTED)) {
		TOGGLE_BIT(victim->state_flags, STATE_MANACLE);
		if (IS_SET(victim->state_flags, STATE_MANACLE)) {
			act("Manacles have been slapped around $n's wrists!!!",
			    victim, NULL, ch, TO_ROOM);
			char_puts
			    ("Manacles have been slapped around your wrists!!!\n",
			     victim);

			af.where = TO_AFFECTS;
			af.type = sn_manacle;
			af.level = ch->level;
			af.duration = (ch->level) / 3;
			af.location = APPLY_STR;
			af.modifier = -1 * ((ch->level) / 3);
			af.bitvector = 0;
			affect_to_char(victim, &af);
			char_puts
			    ("You feel your strength slipping as the manacles tighten around your wrists.\n",
			     victim);
			act("$n looks like his strength is slipping as the manacles tighten around his wrists.", victim, NULL, NULL, TO_ROOM);

			af.where = TO_AFFECTS;
			af.type = sn_manacle;
			af.level = ch->level;
			af.duration = (ch->level) / 3;
			af.location = APPLY_DEX;
			af.modifier = -1 * ((ch->level) / 3);
			af.bitvector = 0;
			affect_to_char(victim, &af);
			char_puts
			    ("You feel the manacles begin to hinder your movements.\n",
			     victim);
			act("$n looks as if the manacles have begun hindering his movements.", victim, NULL, NULL, TO_ROOM);

			af.where = TO_AFFECTS;
			af.type = sn_manacle;
			af.level = ch->level;
			af.duration = (ch->level) / 3;
			af.location = APPLY_HITROLL;
			af.modifier = -1 * (ch->level);
			af.bitvector = 0;
			affect_to_char(victim, &af);
			char_puts
			    ("You feel the manacles hindering your accuracy.\n",
			     victim);
			act("$n looks as if the manacles have begun hindering his accuracy.", victim, NULL, NULL, TO_ROOM);

			af.where = TO_AFFECTS;
			af.type = sn_manacle;
			af.level = ch->level;
			af.duration = (ch->level) / 3;
			af.location = APPLY_DAMROLL;
			af.modifier = -1 * (ch->level);
			af.bitvector = 0;
			affect_to_char(victim, &af);
			char_puts
			    ("You feel the manacles hinder your battle prowress.\n",
			     victim);
			act("$n looks as if the manacles have begun hindering battle prowress.", victim, NULL, NULL, TO_ROOM);

			af.where = TO_AFFECTS;
			af.type = sn_manacle;
			af.level = ch->level;
			af.duration = (ch->level) / 3;
			af.location = APPLY_AC;
			af.modifier = 4 * (ch->level);
			af.bitvector = 0;
			affect_to_char(victim, &af);
			char_puts("You feel like a much easier target.\n",
				  victim);
			act("$n looks like a much easier target.", victim, NULL,
			    NULL, TO_ROOM);
			return;

		} else {
			act("The manacles have been removed from $n's wrists.",
			    victim, NULL, ch, TO_ROOM);
			char_puts
			    ("The manacles have been removed from your wrists.\n",
			     victim);

			for (paf = victim->affected; paf; paf = paf_next) {
				paf_next = paf->next;
				if (paf->type == sn_manacle)
					affect_remove(victim, paf);
			}

			return;
		}
		char_puts("They must be criminals to be manacled.\n", ch);
	}
	char_puts("They must be criminals to be manacled.\n", ch);
}

DO_FUN(do_wanted)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	int sn_wanted;

	if ((sn_wanted = sn_lookup("wanted")) < 0
	    || get_skill(ch, sn_wanted) == 0) {
		char_puts("Huh?\n", ch);
		return;
	}

	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		char_puts("Toggle wanted whom?\n", ch);
		return;
	}

	if ((victim = get_char_world(ch, arg)) == NULL) {
		char_puts("They aren't here.\n", ch);
		return;
	}

	if (IS_NPC(victim)) {
		char_puts("Not a mobile, of course.\n", ch);
		return;
	}

	if (IS_IMMORTAL(victim) && ch->level < victim->level) {
		act("You do not have the power to arrest $N.",
		    ch, NULL, victim, TO_CHAR);
		return;
	}

	if (victim == ch) {
		char_puts("You cannot do that to yourself.\n", ch);
		return;
	}

	TOGGLE_BIT(victim->state_flags, STATE_WANTED);
	if (IS_SET(victim->state_flags, STATE_WANTED)) {
		act("$n is now WANTED!!!", victim, NULL, ch, TO_NOTVICT);
		act("$n is now WANTED!!!", victim, NULL, ch, TO_VICT);
		char_puts("You are now WANTED!!!\n", victim);
	} else {
		act("$n is no longer wanted.", victim, NULL, ch, TO_NOTVICT);
		act("$n is no longer wanted.", victim, NULL, ch, TO_VICT);
		char_puts("You are no longer wanted.\n", victim);
	}
}

/*-----------------------------------------------------------------------------
 * toggle bit stuff
 */
typedef struct toggle_t toggle_t;

struct toggle_t {
	const char *name;	/* flag name                            */
	const char *desc;	/* toggle description                   */
	flag_t *f;		/* flag table                           */
	flag64_t bit;		/* flag bit                             */
	const char *msg_on;	/* msg to print when flag toggled on    */
	const char *msg_off;	/* ---//--- off                         */
};

static toggle_t *toggle_lookup(const char *name);
static void toggle_print(CHAR_DATA * ch, toggle_t * t);
static flag64_t *toggle_bits(CHAR_DATA * ch, toggle_t * t);

/*
 * alphabetize these table by name if you are adding new entries
 */
toggle_t toggle_table[] = {
	/* *INDENT-OFF* */
	{ "affects",		"show affects in score",
	  comm_flags,	COMM_SHOWAFF,
	  "Affects will now be shown in score.",
	  "Affects will no longer be shown in score."
	},

	{ "brief",		"brief descriptions",
	  comm_flags,	COMM_BRIEF,
	  "Short descriptions activated.",
	  "Full descriptions activated."
	},

	{ "color",		"ANSI colors",
	  comm_flags,	COMM_COLOR,
	  "{BC{Ro{Yl{Co{Gr{x is now {RON{x, Way Cool!",
	  "Color is now OFF, *sigh*"
	},

	{ "compact",		"compact mode",
	  comm_flags,	COMM_COMPACT,
	  "$t set.",
	  "$t removed."
	},

	{ "combine",		"combined items in inventory list",
	  comm_flags,	COMM_COMBINE,
	  "Combined inventory selected.",
	  "Long inventory selected."
	},

	{ "long flags",		"long flags mode",
	  comm_flags,	COMM_LONG,
	  "$t set.",
	  "$t removed."
	},

	{ "nobust",		"do not bust prompt if hp/mana/move changed",
	  comm_flags,	COMM_NOBUST,
	  "$t set.",
	  "$t removed."
	},

	{ "noeng",		"do not display english obj/mob names",
	  comm_flags,	COMM_NOENG,
	  "You will not see english obj/mob names anymore.",
	  "You will now see english obj/mob names."
	},

	{ "noflee",		"do not flee from combat in lost-link",
	  comm_flags,	COMM_NOFLEE,
	  "You will not flee automagically from combat in lost-link anymore.",
	  "You will flee automagically from combat in lost-link."
	},

	{ "notelnet",		"no telnet parser",
	  comm_flags,	COMM_NOTELNET,
	  "Telnet parser is OFF.",
	  "Telnet parser is ON.",
	},

	{ "noiac",		"no IACs in output",
	  comm_flags,	COMM_NOIAC,
	  "IACs will not be sent to you anymore.",
	  "Text will be sent to you unmodified.",
	},

	{ "noverbose",		"no verbose messages",
	  comm_flags,	COMM_NOVERBOSE,
	  "You will no longer see verbose messages.",
	  "Now you will see verbose messages."
	},

	{ "prompt",		"show prompt",
	  comm_flags,	COMM_PROMPT,
	  "You will now see prompts.",
	  "You will no longer see prompts."
	},

	{ "telnet GA",		"send IAC GA (goahead) after each prompt",
	  comm_flags,	COMM_TELNET_GA,
	  "IAC GA will be sent after each prompt.",
	  "IAC GA will not be sent after prompts.",
	},

	{ "quiet edit",		"quiet mode in string editor",
	  comm_flags,	COMM_QUIET_EDITOR,
	  "$t set.",
	  "$t removed."
	},

	{ "helper",		"offer to help newbies in need",
	  plr_conf_flags,	PLR_CONF_NEWBIE_HELPER,
	  "$t set.  Do not offensively PK.  You may help newbies in OOC ways.",
	  "$t removed.  You can go back to PKing.  You are bound by your normal RP."
	},

	{ "newbie",	"newbie protection, a trial account for new players",
	  acct_flags,	ACCT_NEWBIE,
	  "$t set.  You are now in newbie-protected mode.  You cannot PK or be PKed.",
	  "$t removed.  You can now join a clan, hold artifacts, PK and be PKed."
	},

	{ "true lifer",	"one life, one death.{x",
	  acct_flags,	ACCT_TRUE_LIFER,
	  "None of this resurrection crap for you.",
	  "You are no longer a true lifer . . . wimp!"
	},

	{ "multikiller",	"forsake multikill rules {D- {rthis cannot be turned off{x",
	  acct_flags,	ACCT_MULTIKILLER,
	  "You may now ignore the multikill rules with other multikillers {RONLY{x.",
	  "You must abide the multikill rules with all players."
	},

	{ NULL }
	/* *INDENT-ON* */
};

DO_FUN(do_toggle)
{
	toggle_t *t;
	char arg[MAX_INPUT_LENGTH];

	argument = one_argument(argument, arg, sizeof(arg));
	if (arg[0] == '\0') {
		char_puts("Your current settings are:\n", ch);
		for (t = toggle_table; t->name; t++)
			toggle_print(ch, t);
		return;
	}

	for (; arg[0]; argument = one_argument(argument, arg, sizeof(arg))) {
		flag64_t *bits;

		if ((t = toggle_lookup(arg)) == NULL
		    || (bits = toggle_bits(ch, t)) == NULL) {
			char_printf(ch, "%s: no such toggle.\n", arg);
			continue;
		}

		if (!IS_IMMORTAL(ch)
		&& t->f == acct_flags
		&& t->bit == ACCT_NEWBIE) {
			char_puts("Newbie protection cannot be turned off,"
				" you must remake your character.\n",
				ch);
			continue;
		}

		if (!IS_IMMORTAL(ch)
		&& t->f == acct_flags
		&& t->bit == ACCT_MULTIKILLER
		&& IS_SET(ch->acct_flags, ACCT_MULTIKILLER)) {
			char_puts("Once a multi-killer always a multi-killer.\n",
			ch);
			continue;
		}

		if (!IS_IMMORTAL(ch)
		&& t->f == acct_flags
		&& t->bit == ACCT_TRUE_LIFER) {
			if (IS_SET(ch->acct_flags, ACCT_TRUE_LIFER))
				char_puts("Only one way out.  This ain't it.\n",
					ch);
			else
				char_puts("This is only selectable at creation.\n",
					ch);
			continue;
		}

		if (IS_IMMORTAL(ch) && ch->level < ML
		&& t->f == acct_flags
		&& t->bit == ACCT_TRUE_LIFER) {
			char_puts("Uhm, yeah.  Whatever.\n", ch);
			continue;
		}

		if (!IS_IMMORTAL(ch)
		&& t->f == acct_flags
		&& t->bit == ACCT_MULTIKILLER
		&& !IS_SET(ch->acct_flags, ACCT_MULTIKILLER)) {
			wiznet("$N is now a {rmultikiller{x.",
				ch, NULL,
				WIZ_ANNOUNCE, 0, 0);
			LOG("%s is now a multikiller", ch->name);
		}


		TOGGLE_BIT(*bits, t->bit);
		act_puts(IS_SET(*bits, t->bit) ? t->msg_on : t->msg_off,
			 ch, t->desc, NULL, TO_CHAR | ACT_TRANS, POS_DEAD);
	}
}

static toggle_t *toggle_lookup(const char *name)
{
	toggle_t *t;

	for (t = toggle_table; t->name; t++)
		if (!str_prefix(name, t->name))
			break;
	return t;
}

static void toggle_print(CHAR_DATA * ch, toggle_t * t)
{
	char buf[MAX_STRING_LENGTH];
	flag64_t *bits;

	if ((bits = toggle_bits(ch, t)) < 0)
		return;

	snprintf(buf, sizeof(buf), "  %-11.11s {D-{x %-7.7s ($t)",
		 t->name, IS_SET(*bits, t->bit) ? "{GON{x" : "{ROFF{x");
	act_puts(buf, ch, t->desc, NULL, TO_CHAR | ACT_TRANS, POS_DEAD);
}

static flag64_t *toggle_bits(CHAR_DATA * ch, toggle_t * t)
{
	if (t->f == comm_flags)
		return &ch->comm;
	if (t->f == acct_flags)
		return &ch->acct_flags;
	if (t->f == state_flags)
		return &ch->state_flags;
	if (t->f == plr_conf_flags)
		return &ch->conf_flags;
	return NULL;
}