roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * communication.cpp
 *	 Functions used for communication on the mud
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "commands.h"
#include "communication.h"
#include "move.h"
#include "clans.h"
#include "guilds.h"

//*********************************************************************
//						confusionChar
//*********************************************************************

char confusionChar() {
	int n;
	switch(mrand(0,10)) {
	case 0:
		// random upper case
		return(mrand(65, 90));
	case 1:
	case 2:
		// random character
		n = mrand(0,57);
		// weight all the characters in the different ranges evenly
		if(n <= 31) {
			// mrand(91, 121);		31 chars
			return(n + 91);
		} else if(n <= 38) {
			// mrand(58, 64);		7 chars
			return(n - 31 + 58);
		} else if(n <= 54) {
			// mrand(32, 47);		16 chars
			return(n - 31 - 7 + 32);
		} else {
			// mrand(123, 125);		3 chars
			return(n - 31 - 7 - 16 + 123);
		}
	case 3:
		// random number
		return(mrand(48, 57));
		break;
	default:
		break;
	}
	// random lower case
	return(mrand(97, 122));
}

//*********************************************************************
//						confusionText
//*********************************************************************
// make the player say gibberish instead of actual words

bstring confusionText(Creature* speaker, bstring text) {
	if(	(!speaker->isEffected("confusion") && !speaker->isEffected("drunkenness")) ||
		speaker->isStaff()
	)
		return(text);

	bool scramble = mrand(0,1);

	for(int i=0; i<text.getLength(); i++) {
		if(text.getAt(i) == ' ') {
			scramble = !mrand(0,3);
			// sometimes scramble spaces, too
			if(!mrand(0,8))
				text.setAt(i, confusionChar());
		} else {

			if(scramble || !mrand(0,8))
				text.setAt(i, confusionChar());

		}
	}
	return(text);
}


//*********************************************************************
//						getFullstrText
//*********************************************************************
// general function that gets a cmnd->fullstr and returns the portion
// of the text that will get sent to other player(s)
// skip = 1 when we only have 1 command to skip
//    Ex: say hello all
// skip = 2 when we have 2 commands to skip
//    Ex: tell bob hello there

bstring getFullstrText(bstring str, int skip, char toSkip, bool colorEscape) {
	int i=0, n = str.length() - 1;

	// first of all, trim both directions
	while(i <= n && str.at(i) == toSkip)
		i++;
	while(n >= 0 && str.at(n) == toSkip)
		n--;

	while(skip > 0) {
		// skip a set of spaces
		for(; i < (int)str.length()-1; i++)
			if(str.at(i) == toSkip && str.at(i+1) != toSkip)
				break;
		i++;
		skip--;
	}


	// these numbers can't overlap
	if(i > n)
		str = "";
	else {
		str = str.substr(0, n+1);
		str = str.substr(i);
	}

	if(colorEscape)
		str = escapeColor(str);
	return(str);
}

//*********************************************************************
//						communicateWith
//*********************************************************************
// This is the base function for communication with a target player.
// The commands tell, whisper, reply, and sign are all routed through here.

int communicateWith(Player* player, cmd* cmnd) {
	bstring text = "";
	Player	*target=0;
	int		i=0, found=0;
	ctag	*cp=0;
	char	ooc_str[10];

	// reuse text
	text = cmnd->myCommand->getName();

	// get us a channel to use!
	commPtr chan = NULL;
	while(commList[i].name != NULL) {
		if(!strcmp(text.c_str(), commList[i].name)) {
			chan = &commList[i];
			break;
		}
		i++;
	}
	if(!chan) {
		player->print("Unknown channel.\n");
		return(0);
	}

	strcpy(ooc_str, (chan->ooc ? "[ooc]: " : ""));


	// extra checks for reply and sign
	if(chan->type == COM_REPLY) {
		strcpy(cmnd->str[1], player->getLastCommunicate().c_str());
		if(!strlen(cmnd->str[1])) {
			player->print("You have nobody to reply to right now.\n");
			return(0);
		}
	} else if(chan->type == COM_SIGN) {
		if(player->getRace() != DARKELF && !player->isStaff()) {
			player->print("Only dark elves may communicate by signing.\n");
			return(0);
		}
	}


	// the basic communication checks all commands must obey
	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		return(0);

	if(cmnd->num < chan->skip) {
		player->print("%s to whom?\n", com_text_u[chan->type]);
		return(0);
	}

	if(!player->flagIsSet(P_SECURITY_CHECK_OK)) {
		player->print("You may not do that yet.\n");
		return(0);
	}

	// silence!
	if(chan->type != COM_SIGN) {
		if(player->flagIsSet(P_DM_SILENCED)) {
			player->printColor("^CYou are unable to move your fingers.\n");
			return(0);
		}
		if(!player->canSpeak()) {
			player->printColor("^yYou cannot speak.\n");
			return(0);
		}
	} else {
		if(player->flagIsSet(P_DM_SILENCED)) {
			player->printColor("^yYou are unable to move your lips.\n");
			return(0);
		}
	}


	// run the player table
	cmnd->str[1][0] = up(cmnd->str[1][0]);
	std::pair<bstring, Player*> p;
	Player* ply;
	foreach(p, gServer->players) {
		ply = p.second;

		if(!ply->isConnected())
			continue;
		// these two need vicinity
		if(chan->type == COM_SIGN || chan->type == COM_WHISPER)
			if(!player->inSameRoom(ply))
				continue;
		if(!player->canSee(ply))
			continue;
		if(!strcmp(ply->name, cmnd->str[1])) {
			target = ply;
			break;
		}
		if(!strncmp(ply->name, cmnd->str[1], strlen(cmnd->str[1]))) {
			target = ply;
			found++;
		}
	}

	if(found > 1) {
		player->print("More than one person with that name.\n");
		return(0);
	}

	if(!target || !player->canSee(target)) {
		player->print("%s to whom?\n", com_text_u[chan->type]);
		return(0);
	}



	// higher level communication checks
	if(player == target && !player->flagIsSet(P_CAN_FLASH_SELF) && !player->isStaff()) {
		player->print("Talking to yourself is a sign of insanity.\n");
		return(0);
	}

	if(player->getClass() == BUILDER && !target->isStaff()) {
		player->print("You may only communicate with staff!\n");
		return(0);
	}

	// polite ignore message for watchers
	if(!player->isCt() && target->flagIsSet(P_NO_TELLS)) {
		if(target->isPublicWatcher())
			player->print("%s is busy at the moment. Please mudmail %s.\n", target->name, target->himHer());
		else
			player->print("%s is ignoring everyone.\n", target->name);
		return(0);
	}


	// staff can always message AFK people
	if(	target->flagIsSet(P_AFK) &&
		!player->checkStaff("%s is currently afk.\n", target->name))
		return(0);

	if(chan->type != COM_SIGN && target->isEffected("deafness") && !player->isStaff()) {
		player->print("%s is deaf and cannot hear anything.\n", target->name);
		return(0);
	}


	if(	target->isIgnoring(player->name) &&
		!target->checkStaff("%s is ignoring you.\n", target->name)
	)
		return(0);

	if(chan->type == COM_SIGN || chan->type == COM_WHISPER)
		player->unhide();

	if(chan->type == COM_SIGN) {
		if(!target->isStaff()) {
			if(target->getRace() != DARKELF && target->getClass() < BUILDER) {
				player->print("%s doesn't understand dark elven sign language.\n", target->upHeShe());
				return(0);
			}
			if(target->isEffected("blindness")) {
				player->print("%M is blind! %s can't see what you're signing.\n", target, target->upHeShe());
				return(0);
			}
			if(player->isInvisible() && !target->isEffected("detect-invisible")) {
				player->print("You are invisible! %s cannot see what you are signing.\n", target->name);
				return(0);
			}
		}
	} else {
		if(	!target->languageIsKnown(LUNKNOWN+player->current_language) &&
			!player->isStaff()
		) {
			player->print("%s doesn't speak your current language.\n", target->upHeShe());
			return(0);
		}
	}

	// spam check
	if(player->checkForSpam())
		return(0);


	// reuse text
	text = getFullstrText(cmnd->fullstr, chan->skip, ' ', true);
	if(text == "") {
		player->print("%s what?\n", com_text_u[chan->type]);
		return(0);
	}
	text = confusionText(player, text);

	player->printColor("^cYou %s \"%s%s\" to %N.\n",
		com_text[chan->type],
		ooc_str, text.c_str(), target);


	if(chan->type == COM_WHISPER)
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M whispers something to %N.", player, target);


	if(!target->isGagging(player->name)) {
		target->printColor("%s%s%M %s%s, \"%s%s\".\n",
			target->customColorize("*CC:TELL*"),
			chan->type == COM_WHISPER || chan->type == COM_SIGN ? "" : "### ",
			player,
			chan->type == COM_WHISPER || chan->type == COM_SIGN ? com_text[chan->type] : "just flashed",
			chan->type == COM_WHISPER || chan->type == COM_SIGN ? " to you" : "",
			ooc_str, text.c_str());
	} else {
		if(!player->isDm() && !target->isDm())
			broadcast(watchingEaves, "^E--- %s TRIED sending to %s, \"%s\".",
				player->name, target->name, text.c_str());
	}


	// sign doesnt use reply anyway
	if(chan->type != COM_SIGN) {

		if(!target->isStaff() &&
			( ( player->flagIsSet(P_INCOGNITO) && !player->inSameRoom(target) ) ||
				( player->isInvisible() && !target->isEffected("detect-invisible") ) ||
				( player->flagIsSet(P_MISTED) && !target->isEffected("true-sight") ) ||
				player->flagIsSet(P_DM_INVIS) ||
		    	target->flagIsSet(P_UNCONSCIOUS) ||
		    	target->isBraindead() ||
		    	target->flagIsSet(P_LINKDEAD)
		    )
		) {
			player->print("%s will be unable to reply.", target->upHeShe());
			if(target->flagIsSet(P_LINKDEAD))
				player->print(" %s is linkdead.", target->upHeShe());
			player->print("\n");

			if(player->flagIsSet(P_ALIASING))
				player->print("Sent from: %s.\n", player->getAlias()->name);
		} else if(player->flagIsSet(P_IGNORE_ALL)) {
			player->print("You are ignoring all, as such they will be unable to respond.\n");
		}

	} else {

		cp = player->getRoom()->first_ply;
		while(cp) {
			if(cp->crt != player && cp->crt != target &&
			        !cp->crt->isEffected("blindness")) {
				if(cp->crt->getRace() == DARKELF || cp->crt->isStaff())
					cp->crt->print("%M signed, \"%s\" to %N.\n", player, text.c_str(), target);
				else
					cp->crt->print("%M signed something in dark elven to %N.\n", player, target);
			}
			cp = cp->next_tag;
		}

	}

	if(player->isDm() || target->isDm())
		return(0);

	broadcast(watchingEaves, "^E--- %s %s to %s, \"%s%s\".", player->name,
		com_text[chan->type], target->name, ooc_str, text.c_str());

	player->bug("%s %s, \"%s%s\" to %s.\n", player->name,
		com_text[chan->type], ooc_str, text.c_str(), target->name);
	target->bug("%s %s, \"%s%s\" to %s.\n", player->name,
		com_text[chan->type], ooc_str, text.c_str(), target->name);

	if(chan->type != COM_SIGN)
		target->setLastCommunicate(player->name);
	return(0);
}


//*********************************************************************
//						commTarget
//*********************************************************************
// function that does the actual printing of message for below

void commTarget(Creature* player, Player* target, int type, bool ooc, char lang, bstring text, bstring speak, char *ooc_str, bool anon) {
	std::ostringstream out;

	if(!target || target->flagIsSet(P_UNCONSCIOUS))
		return;
	if(target->isEffected("deafness") && !player->isStaff())
		return;

	if(type != COM_GT)
		speak += "s";

	if(type == COM_EMOTE) {
		out << player->getCrtStr(target, CAP) << " " << text << "\n";
		target->bug("%s emoted: %s.\n", player->name, text.c_str());

	} else if( ooc ||
		target->languageIsKnown(LUNKNOWN+lang) ||
		target->isStaff() ||
		target->isEffected("comprehend-languages")
	) {

		if(type == COM_GT)
			out << target->customColorize("*CC:GROUP*") << "### ";

		if(ooc || lang == LCOMMON) {
			if(anon) out << "Someone";
			else	 out << player->getCrtStr(target, CAP);
			out << " " << speak;
		} else {
			if(anon) out << "Someone";
			else	 out << player->getCrtStr(target, CAP);

			out << " " << speak << " in " << get_language_adj(lang);
		}
		out << ", \"";

//		if(!ooc && target->flagIsSet(P_LANGUAGE_COLORS))
//			ANSI(fd, get_lang_color(lang));
		out << ooc_str << text;
		out << "\".\n";

		target->bug("%s %s in %s, \"%s%s.\"\n", player->name, com_text[type],
			get_language_adj(lang), ooc_str, text.c_str());

	} else {
		if(anon) out << "Someone";
		else	 out << player->getCrtStr(target, CAP);

		out << " " << speak << " something in " << get_language_adj(lang) << ".\n";
		target->bug("%s %s something in %s.\n", player->name, speak.c_str(), get_language_adj(lang));
	}

	target->printColor("%s", out.str().c_str());
}

//*********************************************************************
//						communicate
//*********************************************************************
// This function allows the player to say something to all the other
// people in the room (or nearby rooms).

int pCommunicate(Player* player, cmd* cmnd) {
	return(communicate(player, cmnd));
}

int communicate(Creature* creature, cmd* cmnd) {
	const Player* player=0;
	bstring text = "", name = "";
	Creature *owner=0, *target=0;
	Player*	pTarget=0;
	AreaRoom* aRoom=0;
	Room	*new_rom=0;
	int		i=0;
	char	speak[35], lang, ooc_str[10];
	ctag	*cp=0;
	xtag	*xp=0;

	if(creature->isPet())
		owner = creature->following;
	else
		owner = creature;
	player = owner->getConstPlayer();

	// reuse text
	text = cmnd->myCommand->getName();

	// get us a channel to use!
	sayPtr chan = NULL;
	while(sayList[i].name != NULL) {
		if(!strcmp(text.c_str(), sayList[i].name)) {
			chan = &sayList[i];
			break;
		}
		i++;
	}
	if(!chan) {
		creature->print("Unknown channel.\n");
		return(0);
	}

	strcpy(ooc_str, (chan->ooc ? "[ooc]: " : ""));
	owner->clearFlag(P_AFK);

	if(!creature->ableToDoCommand())
		return(0);

	if(!creature->canSpeak() || (owner && owner != creature && !owner->canSpeak())) {
		creature->printColor("^yYour lips move but no sound comes forth.\n");
		return(0);
	}
	// reuse text
	text = getFullstrText(cmnd->fullstr, 1, ' ', true);
	if(text == "") {
		creature->print("%s what?\n", com_text_u[chan->type]);
		return(0);
	}
	text = confusionText(owner, text);

	lang = owner->current_language;

	if(chan->ooc)
		strcpy(speak, "say");
	else if(chan->type == COM_RECITE)
		strcpy(speak, "recite");
	else if(chan->type == COM_GT)
		strcpy(speak, "group mentioned");
	else if(chan->type == COM_YELL) {
		strcpy(speak, "yell");
		text += "!";
	} else
		strcpy(speak, get_language_verb(lang));


	// GT uses their leader's pointer to find the people we're talking
	// to while everything else uses the room pointer
	if(chan->type == COM_GT) {

		cp = creature->first_fol;
		if(cp)
			target = cp->crt;

		if(	(!creature->following && !creature->first_fol) ||
			(target && target->isPlayer() && target->flagIsSet(P_DM_INVIS) && !target->flagIsSet(P_INCOGNITO))
		) {
			creature->print("You are not in a group.\n");
			return(0);
		}

		target = (creature->following ? creature->following : creature);
		cp = target->first_fol;

		commTarget(creature, target->getPlayer(), chan->type, chan->ooc, lang, text, speak, ooc_str, false);

	} else {
		cp = creature->getRoom()->first_ply;
		creature->unhide();
	}

	// GT prints to the leader above
	if(chan->type != COM_GT) {
		if(chan->type == COM_EMOTE) {

			creature->printColor("You emote: %s.\n", text.c_str());
			player->bug("%s emoted: %s.\n", creature->name, text.c_str());

		} else {
			char intro[2046];
			if(chan->ooc || lang == LCOMMON)
				sprintf(intro, "You %s,", speak);
			else
				sprintf(intro, "You %s in %s,", speak, get_language_adj(lang));

			if(!chan->ooc && creature->flagIsSet(P_LANGUAGE_COLORS))
				ANSI(creature->getSock(), get_lang_color(lang));
			creature->printColor("%s \"%s%s\"^x.\n", intro, ooc_str, text.c_str());
			player->bug("%s %s in %s, \"%s%s.\"\n", creature->name, com_text[chan->type],
				get_language_adj(lang), ooc_str, text.c_str());

		}
	}


	while(cp) {
		pTarget = cp->crt->getPlayer();
		cp = cp->next_tag;

		if(!pTarget)
			continue;

		if(chan->shout)
			pTarget->wake("Loud noises disturb your sleep.", true);

		// GT prints to the player!
		if(pTarget == creature && chan->type != COM_GT)
			continue;

		if(pTarget->isGagging(creature->isPet() ? creature->following->name : creature->name))
			continue;

		commTarget(creature, pTarget, chan->type, chan->ooc, lang, text, speak, ooc_str, false);
	}

	if(chan->shout) {
		// Because of multiple exits leading to the same room, we will keep
		// track of who has heard us shout
		std::list<Socket*> listeners;
		std::list<Socket*>::iterator it;
		bool	heard = false;

		// This allows a player to yell something that will be heard
		// not only in their room, but also in all rooms adjacent to them. In
		// the adjacent rooms, however, people will not know who yelled.
		xp = creature->getRoom()->first_ext;
		while(xp) {
			aRoom = 0;
			new_rom = 0;
			i=0;
			cp=0;

			// don't shout through closed doors
			if(!xp->ext->flagIsSet(X_CLOSED))
				Move::getRoom(0, xp->ext, &new_rom, &aRoom, true);

			xp = xp->next_tag;

			// the same-room checks aren't run in getRoom because
			// we don't send a creature.
			if(aRoom) {
				if(creature->area_room && aRoom == creature->area_room)
					continue;
				cp = aRoom->first_ply;
			} else if(new_rom) {
				if(creature->parent_rom && new_rom == creature->parent_rom)
					continue;
				cp = new_rom->first_ply;
			} else
				continue;

			while(cp) {
				pTarget = cp->crt->getPlayer();
				cp = cp->next_tag;

				if(!pTarget)
					continue;

				pTarget->wake("Loud noises disturb your sleep.", true);
				if(pTarget->isGagging(creature->name))
					continue;

				// have they already heard us yell?
				heard = false;

				for(it = listeners.begin() ; it != listeners.end() ; it++) {
					if((*it) == pTarget->getSock()) {
						heard = true;
						break;
					}
				}

				if(!heard) {
					listeners.push_back(pTarget->getSock());
					commTarget(creature, pTarget, chan->type, chan->ooc, lang, text, speak, ooc_str, true);
				}
			}
		}
		listeners.clear();
	}

	if(chan->passphrase) {
		xp = creature->getRoom()->first_ext;
		while(xp) {
			// got the phrase right?
			if(xp->ext->getPassPhrase() != "" && xp->ext->getPassPhrase() == text) {
				// right language?
				if(!xp->ext->getPassLanguage() || lang == xp->ext->getPassLanguage()) {
					// even needs to be open?
					if(xp->ext->flagIsSet(X_LOCKED)) {
						broadcast(NULL, creature->getRoom(), "The %s opens!", xp->ext->name);
						xp->ext->clearFlag(X_LOCKED);
						xp->ext->clearFlag(X_CLOSED);

						if(xp->ext->getOpen() != "") {
							if(xp->ext->flagIsSet(X_ONOPEN_PLAYER)) {
								creature->print("%s.\n", xp->ext->getOpen().c_str());
							} else {
								broadcast(0, creature->getRoom(), xp->ext->getOpen().c_str());
							}
						}

					}
				}
			}
			xp = xp->next_tag;
		}
	}


	// DMs still broadcast to eaves on GT
	if(creature->isDm() && chan->type != COM_GT)
		return(0);


	if(creature->isPet()) {
		name = creature->following->name;
		name += "'s ";
		name += creature->name;
	} else
		name = creature->name;

	if(chan->type == COM_EMOTE)
		broadcast(watchingSuperEaves, "^E--- %s %s.", name.c_str(), text.c_str());
	else
		broadcast(watchingSuperEaves, "^E--- %s %s, \"%s%s\" in %s.", name.c_str(), com_text[chan->type],
			ooc_str, text.c_str(), get_language_adj(lang));

	Player* pPlayer = creature->getPlayer();
	// spam check
	if(pPlayer)
		pPlayer->checkForSpam();

	return(0);
}

//*********************************************************************
//						channel
//**********************************************************************
// This function is used as a base for all global communication channels

int channel(Player* player, cmd* cmnd) {
	bstring text = "", chanStr = "", extra = "";
	int		i=0, check=0, skip=1;
	Guild* guild=0;

	player->clearFlag(P_AFK);
	chanStr = cmnd->myCommand->getName();
	i = strlen(cmnd->str[1]);

	// these require special attention - we most provide an
	// override for the check below
	if(chanStr == "dmcls" || chanStr == "dmclass") {

		chanStr = "classsend";
		skip = 2;

		for(check=1; check<CLASS_COUNT-1; check++)
			if(!strncasecmp(get_class_string(check), cmnd->str[1], i))
				break;

		// these checks are overkill, but it never hurts to be safe:
		// this will force them to use their own class
		if(check == DUNGEONMASTER && !player->isDm())
			check = 0;
		if(check == CARETAKER && !player->isCt())
			check = 0;

	} else if(chanStr == "dmrace") {

		chanStr = "racesend";
		skip = 2;

		for(check=1; check<gConfig->raceCount()-1; check++)
			if(!strncasecmp(gConfig->getRace(check)->getName().c_str(), cmnd->str[1], i))
				break;

	} else if(chanStr == "dmclan") {

		chanStr = "clansend";
		skip = 2;

		std::map<int, Clan*>::iterator it;
		Clan *clan=0;

		for(it = gConfig->clans.begin() ; it != gConfig->clans.end() ; it++) {
			clan = (*it).second;
			if(!strncasecmp(cmnd->str[1], clan->getName().c_str(), i)) {
				check = clan->getId();
				break;
			}
		}

	} if(chanStr == "dmguild") {

		skip = 2;
		// 0 = not found, -1 = not unique
		check = 0;

		std::map<int, Guild*>::iterator it;
		for(it = gConfig->guilds.begin(); it != gConfig->guilds.end(); it++) {

			if((*it).second->name.left(i).toLower() == cmnd->str[1]) {
				if(!check) {
					check = 1;
					guild = (*it).second;
				} else
					check = -1;
			}
		}

		if(!check) {
			player->print("Guild not found.\n");
			return(0);
		} else if(check == -1) {
			player->print("Guild name was not unique.\n");
			return(0);
		}
	}



	text = getFullstrText(cmnd->fullstr, skip);
	if(text == "") {
		player->print("Send what?\n");
		return(0);
	}
	text = confusionText(player, text);


	// this isnt a channel in the list
	if(chanStr == "dmguild") {
		doGuildSend(guild, player, text);
		return(0);
	}


	// get us a channel to use!
	channelPtr chan = NULL;
	i = 0;
	while(channelList[i].channelName != NULL) {
		if(	!strcmp(chanStr.c_str(), channelList[i].channelName) &&
			(!channelList[i].canSee || channelList[i].canSee(player))
		) {
			chan = &channelList[i];
			break;
		}
		i++;
	}

	if(!chan)
		return(cmdNoExist(player, cmnd));


	// get us that extra text we'll be attaching to the front of the message
	if(chan->type == COM_CLASS) {

		if(!check)
			check = player->getClass();
		extra = "(";
		extra += get_class_string(check);
		if(player->getDeity() && gConfig->classes[get_class_string(check)]->needsDeity()) {
			extra += ":";
			extra += gConfig->getDeity(player->getDeity())->getName();
		}
		extra += ") ";

	} else if(chan->type == COM_RACE) {

		if(!check)
			check = player->getDisplayRace();
		extra = "(";
		extra += gConfig->getRace(check)->getName().c_str();
		extra += ") ";

	} else if(chan->type == COM_CLAN) {

		if(!player->getClan() && !player->getDeity() && !check) {
			player->print("You do not belong to a clan or religion.\n");
			return(0);
		}

		extra = "(";
		if(!check) {
			if(player->getDeity()) {
				check = player->getDeityClan();
				extra += gConfig->getDeity(player->getDeity())->getName();
			} else {
				check = player->getClan();
				extra += gConfig->getClan(player->getClan())->getName();
			}
		} else
			extra += gConfig->getClan(check)->getName();
		extra += ") ";

	}


	// it is up to the canUse function to print out the error message
	if(chan->canUse && !chan->canUse(player))
		//oldPrint(fd, "You are not authorized to use that channel.\n");
		return(0);
	if(chan->maxLevel != -1 && player->getLevel() > chan->maxLevel && !player->isWatcher()) {
		player->print("You are too high of a level to use that channel.\n");
		return(0);
	}
	if(chan->minLevel != -1 && player->getLevel() < chan->minLevel && !player->isWatcher()) {
		player->print("You must be level %d to use the %s channel.\n", chan->minLevel, chan->channelName);
		return(0);
	}



	if(player->flagIsSet(P_GLOBAL_GAG) && !player->isStaff()) {
		// global gag - don't let them know they're gagged!
		if(extra != "")
			player->printColor("%s%s", player->customColorize(chan->color), extra.c_str());

		player->printColor(player->customColorize(chan->color + chan->displayFmt), player, text.c_str());
	} else {

		std::ostringstream eaves;
		eaves << "--- " << extra << player->name << " ";
		if(chan->type == COM_CLASS)
			eaves << "class";
		else if(chan->type == COM_RACE)
			eaves << "race";
		else if(chan->type == COM_CLAN)
			eaves << "clan";
		eaves << " sent, \"" << text << "\".\n";

		bstring etxt = eaves.str();
		etxt = escapeColor(etxt);
		text = escapeColor(text);

		// more complicated checks go here
		std::pair<bstring, Player*> p;
		Player* ply=0;
		Socket* sock=0;
		foreach(p, gServer->players) {
			ply = p.second;

			sock = ply->getSock();
			if(!sock->isConnected())
				continue;

			// no gagging staff!
			if(player && ply->isGagging(player->name) && !player->isCt())
				continue;
			// deaf people can always hear staff and themselves
			if(ply->isEffected("deafness") && !player->isStaff() && ply != player)
				continue;

			// must satisfy all the basic canHear rules to hear this channel
			if(	(	(!chan->canHear || chan->canHear(sock)) &&
					(!chan->flag || ply->flagIsSet(chan->flag)) &&
					(!chan->not_flag || !ply->flagIsSet(chan->not_flag))
				) && ( // they must also satisfy any special conditions here
					(chan->type != COM_CLASS || ply->getClass() == check) &&
					(chan->type != COM_RACE || ply->getDisplayRace() == check) &&
					(chan->type != COM_CLAN || (ply->getDeity() ? ply->getDeityClan() : ply->getClan()) == check)
			) ) {
				if(extra != "")
					ply->printColor("%s%s", ply->customColorize(chan->color), extra.c_str());

				ply->printColor(ply->customColorize(chan->color + chan->displayFmt), player, text.c_str());
				ply->print("\n");

			}


			// even if they fail the check, it might still show up on eaves
			if(	chan->eaves &&
				watchingEaves(sock) &&
				!(chan->type == COM_CLASS && check == DUNGEONMASTER)
			) {
				ply->printColor("^E%s", etxt.c_str());
			}
		}
	}


// TODO: Maybe put languages back in
	//oldPrint(fd, "You are using the '%s' channel!\n", chanStr.c_str());

//	} else {
//		for(a=0;a<Tablesize;a++) {
//			if(!Ply[a].ply)
//				continue;
//
//			if(is_gag_ply(player->name, Ply[a].ply))
//				continue;
//
//			if(Ply[a].ply->flagIsSet(P_NO_BROADCASTS))
//				continue;
//
//			if(Ply[a].ply->isStaff() || (Ply[a].ply->isEffected("comprehend-languages") && player->current_language)) {
//				if(Ply[a].ply->flagIsSet(P_LANGUAGE_COLORS) && player->current_language != LCOMMON)
//					ANSI(Ply[a].ply->fd, get_lang_color(player->current_language));
//
//				if(player->current_language == LCOMMON)
//					oldPrint(Ply[a].ply->fd, "### %M broadcasted, \"%s\"\n", player, &cmnd->fullstr[i+1]);
//				else
//					oldPrint(Ply[a].ply->fd, "### %M broadcasted, \"%s\" in %s.\n",
//					      player, &cmnd->fullstr[i+1], get_language_adj(player->current_language));
//
//
//			} else {
//				if(!Ply[a].ply->languageIsKnown(LUNKNOWN+player->current_language)) {
//					oldPrint(Ply[a].ply->fd, "### %M broadcasted something in %s.\n", player, get_language_adj(player->current_language));
//				} else {
//					if(Ply[a].ply->flagIsSet(P_LANGUAGE_COLORS) && player->current_language != LCOMMON )
//						ANSI(Ply[a].ply->fd, get_lang_color(player->current_language));
//					if(player->current_language == LCOMMON)
//						oldPrint(Ply[a].ply->fd, "### %M broadcasted, \"%s\"\n", player, &cmnd->fullstr[i+1]);
//					else
//						oldPrint(Ply[a].ply->fd, "### %M broadcasted, \"%s\" in %s.\n",
//						     player, &cmnd->fullstr[i+1], get_language_adj(player->current_language));
//
//				}
//			}
//
//			ANSI(Ply[a].ply->fd, getMainTextColor());
//
//		}// end for
//	}
//
//	player->bug("%s broadcasted, \"%s\" in %s.\n",
//	       player->name, &cmnd->fullstr[i+1], get_language_adj(player->current_language));

	return(0);
}


//*********************************************************************
//						cmdSpeak
//*********************************************************************

int cmdSpeak(Player* player, cmd* cmnd) {
	int		lang=0;

	if(!player->ableToDoCommand())
		return(0);

	if(cmnd->num < 2 ) {
		player->print("Speak what?\n");
		return(0);
	}

	lowercize(cmnd->str[1],0);

	switch (cmnd->str[1][0]) {

	case 'a':
		switch (cmnd->str[1][1]) {
		case 'b':
			lang = LABYSSAL;
			break;
		case 'l':
			lang = 0;
			break;
		case 'r':
			lang = LARCANIC;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'b':
		lang = LBARBARIAN;
		break;

	case 'c':
		switch (cmnd->str[1][1]) {
		case 'a':
			lang = LINFERNAL;
			break;
		case 'e':
			lang = LCELESTIAL;
			break;
		case 'o':
			lang = LCOMMON;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'd':
		switch (cmnd->str[1][1]) {
		case 'a':
			lang = LDARKELVEN;
			break;
		case 'r':
			lang = LDRUIDIC;
			break;
		case 'w':
			lang = LDWARVEN;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'e':
		lang = LELVEN;
		break;
	case 'g':
		switch (cmnd->str[1][1]) {
		case 'i':
			lang = LGIANTKIN;
			break;
		case 'n':
			lang = LGNOMISH;
			break;
		case 'o':
			lang = LGOBLINOID;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'h':
		switch (cmnd->str[1][1]) {
		case 'u':
			lang = LCOMMON;
			break;
		default:
			switch (cmnd->str[1][4]) {
			case 'e':
				player->print("Half elves have no language of their own.\n");
				player->print("They speak the elven or common tongues.\n");
				return(0);
				break;
			case 'o':
				player->print("Half orcs have no language of their own.\n");
				player->print("They speak the orcish or common tongues.\n");
				return(0);
				break;
			case 'l':
				lang = LHALFLING;
				break;
			case 'g':
				lang = LGIANTKIN;
				break;
			default:
				player->print("You do not know that language.\n");
				return(0);
				break;
			}
			break;
		}
		break;
	case 'i':
		lang = LINFERNAL;
		break;
	case 'k':
		switch (cmnd->str[1][1]) {
		case 'a':
			lang = LKATARAN;
			break;
		case 'o':
			lang = LKOBOLD;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'm':
		lang = LMINOTAUR;
		break;
	case 'o':
		switch (cmnd->str[1][1]) {
		case 'g':
			lang = LOGRISH;
			break;
		case 'r':
			lang = LORCISH;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 's':
		switch (cmnd->str[1][1]) {
		case 'e':
			lang = LCELESTIAL;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 't':
		switch (cmnd->str[1][1]) {
		case 'i':
			lang = LTIEFLING;
			break;
		case 'r':
			lang = LTROLL;
			break;
		case 'h':
			lang = LTHIEFCANT;
			break;
		default:
			player->print("You do not know that language.\n");
			return(0);
			break;
		}
		break;
	case 'w':
		lang = LWOLFEN;
		break;
	default:
		player->print("You do not know that language.\n");
		return(0);
		break;
	}


	if(lang == player->current_language) {
		player->print("You're already speaking %s!\n", get_language_adj(lang));
		return(0);
	}

	if(!player->isEffected("tongues") && !player->languageIsKnown(LUNKNOWN+lang) && !player->isStaff()) {
		player->print("You do not know how to speak %s.\n", get_language_adj(lang));
		return(0);
	} else {
		player->print("You will now speak in %s.\n", get_language_adj(lang));
		player->current_language = lang;
	}

	return(0);
}



//*********************************************************************
//						cmdLanguages
//*********************************************************************

int cmdLanguages(Player* player, cmd* cmnd) {
	char    str[2048];
	//	char	lang[LANGUAGE_COUNT][32];
	int     i, j=0;

	player->print("Currently speaking: %s.\n", get_language_adj(player->current_language));
	player->print("Languages known:");

	strcpy(str," ");
	for(i = 0; i < LANGUAGE_COUNT ; i++) {
		if(player->languageIsKnown(LUNKNOWN + i)) {
			j++;
			strcat(str, get_language_adj(i));
			strcat(str, ", ");
		}
	}

	if(!j)
		strcat(str, "None.");
	else {
		str[strlen(str) - 2] = '.';
		str[strlen(str) - 1] = 0;
	}

	player->print("%s\n", str);

	if(player->isEffected("tongues"))
		player->printColor("^yYou have the ability to speak any language.\n");

	if(player->isEffected("comprehend-languages"))
		player->printColor("^yYou have the ability to comprehend any language.\n");

	return(0);
}


//*********************************************************************
//						printForeignTongueMsg
//*********************************************************************

void printForeignTongueMsg(BaseRoom *inRoom, Creature *talker) {
	ctag		*cp = 0;
	int			lang=0;

	if(!talker || !inRoom)
		return;

	lang = talker->current_language;
	if(!lang)
		return;

	cp = inRoom->first_ply;
	while(cp) {
		if(cp->crt->languageIsKnown(lang) || cp->crt->isEffected("comprehend-languages") || cp->crt->isStaff()) {
			cp = cp->next_tag;
			continue;
		}
		cp->crt->print("%M says something in %s.\n", talker, get_language_adj(lang));

		cp = cp->next_tag;
	}
}



//*********************************************************************
//						canCommunicate
//*********************************************************************

bool canCommunicate(Player* player) {
	if(player->getClass() == BUILDER) {
		player->print("You are not allowed to broadcast.\n");
		return(false);
	}

	// Non staff checks on global communication
	if(!player->isStaff()) {
		if(!player->ableToDoCommand())
			return(false);
		if(player->flagIsSet(P_CANT_BROADCAST)) {
			player->print("Due to abuse, you no longer have that privilage.\n");
			return(false);
		}
		if(player->inJail()) {
			player->print("People in jail do not have that privilage.\n");
			return(false);
		}

		if(player->flagIsSet(P_OUTLAW)) {
			player->print("You're an outlaw, you don't have that privilage.\n");
			return(false);
		}
		if(!player->canSpeak()) {
			player->printColor("^yYour voice is too weak to do that.\n");
			return(false);
		}
		// spam check
		if(player->checkForSpam())
			return(false);

	}
	return(true);
}



//*********************************************************************
//						bstring list functions
//*********************************************************************

int listWrapper(Player* player, cmd* cmnd, const char* gerund, const char* noun, bstring (Player::*show)(void) const, bool (Player::*is)(bstring name) const, void (Player::*del)(bstring name), void (Player::*add)(bstring name), void (Player::*clear)(void)) {
	Player	*target=0;
	bool online=true;

	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		return(0);

	if(cmnd->num == 1) {
		player->print("You are %s: %s\n", gerund, (player->*show)().c_str());
		return(0);
	}

	cmnd->str[1][0] = up(cmnd->str[1][0]);

	if((player->*is)(cmnd->str[1])) {
		(player->*del)(cmnd->str[1]);
		player->print("%s removed from your %s list.\n", cmnd->str[1], noun);
		return(0);
	}

	if(cmnd->num == 2) {
		if(!strcmp(cmnd->str[1], "clear")) {
			(player->*clear)();
			player->print("Cleared.\n");
			return(0);
		}
	}

	target = gServer->findPlayer(cmnd->str[1]);

	if(!target) {
		loadPlayer(cmnd->str[1], &target);
		online = false;
	}

	if(!target || player == target) {
		player->print("That player does not exist.\n");
	} else if(target->isStaff() && target->getClass() > player->getClass()) {
		player->print("You cannot %s that player.\n", noun);
		if(!online)
			free_crt(target);
	} else {

		if(!strcmp(noun, "watch")) {
			if(	!in_group(player, target->name) &&
				!player->isCt() &&
				!(player->isWatcher() && target->getLevel() <= 4)
			) {
				player->print("%M is not a member of your group.\n", target);
				if(!online)
					free_crt(target);
				return(0);
			}
		}

		(player->*add)(cmnd->str[1]);
		player->print("%s added to your %s list.\n", target->name, noun);
		if(!online)
			free_crt(target);
	}

	return(0);
}

int cmdIgnore(Player* player, cmd* cmnd) {
	return(listWrapper(player, cmnd, "ignoring", "ignore",
		&Player::showIgnoring,
		&Player::isIgnoring,
		&Player::delIgnoring,
		&Player::addIgnoring,
		&Player::clearIgnoring
	));
}
int cmdGag(Player* player, cmd* cmnd) {
	return(listWrapper(player, cmnd, "gaging", "gag",
		&Player::showGagging,
		&Player::isGagging,
		&Player::delGagging,
		&Player::addGagging,
		&Player::clearGagging
	));
}
int cmdRefuse(Player* player, cmd* cmnd) {
	return(listWrapper(player, cmnd, "refusing", "refuse",
		&Player::showRefusing,
		&Player::isRefusing,
		&Player::delRefusing,
		&Player::addRefusing,
		&Player::clearRefusing
	));
}
int cmdWatch(Player* player, cmd* cmnd) {
	return(listWrapper(player, cmnd, "watching", "watch",
		&Player::showWatching,
		&Player::isWatching,
		&Player::delWatching,
		&Player::addWatching,
		&Player::clearWatching
	));
}

//*********************************************************************
//						dmGag
//*********************************************************************

int dmGag(Player* player, cmd* cmnd) {
	Player	*target;

	if(!player->isStaff() && !player->isWatcher())
		return(cmdNoExist(player, cmnd));
	if(!player->isWatcher())
		return(cmdNoAuth(player));

	if(cmnd->num < 2) {
		player->print("\nGlobally gag whom?\n");
		return(PROMPT);
	}

	lowercize(cmnd->str[1], 1);
	target = gServer->findPlayer(cmnd->str[1]);

	if(!target) {
		player->print("%s is not on.\n", cmnd->str[1]);
		return(0);
	}

	if(target->flagIsSet(P_GLOBAL_GAG)) {
		player->print("%s is no longer globally gagged.\n", target->name);
		broadcast(isWatcher, "^C*** %s is no longer globally gagged.", target->name);
		broadcast(isDm, "^g*** %s turned off %s's global gag.", player->name, target->name);
		target->clearFlag(P_GLOBAL_GAG);
		return(0);
	}

	if(target->isCt()) {
		target->printColor("^R%s tried to gag you!\n", player->name);
		player->print("You can't globally gag a DM or CT.\n");
		return(0);
	}

	logn("log.gag", "%s was globally gagged by %s.\n", target->name, player->name);
	target->setFlag(P_GLOBAL_GAG);

	broadcast(isWatcher, "^C*** %s has been globally gagged.", target->name);
	broadcast(isDm, "^g*** %s globally gagged %s.", player->name, target->name);
	return(0);
}