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/
/*
 * groups.cpp
 *	 Functions dealing with player groups.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 <sstream>
#include <iomanip>
#include <locale>

//*********************************************************************
//						cmdFollow
//*********************************************************************
// This command allows a player (or a monster) to follow another player.
// Follow loops are not allowed; i.e. you cannot follow someone who is
// following you.

int cmdFollow(Player* player, cmd* cmnd) {
	Player*	toFollow=0;

	player->clearFlag(P_AFK);

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

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


	if(player->flagIsSet(P_SITTING)) {
		player->print("You need to stand first.\n");
		return(0);
	}

	player->unhide();
	lowercize(cmnd->str[1], 1);
	toFollow = player->getRoom()->findPlayer(player, cmnd);
	if(!toFollow) {
		player->print("No one here by that name.\n");
		return(0);
	}

	if(toFollow == player && !player->following) {
		player->print("You can't follow yourself.\n");
		return(0);
	}



	/*	if(player->flagIsSet(P_MISTED)) {
	player->print("You cannot follow someone while misted.\n");
	return(0);
	} */

	if(toFollow->flagIsSet(P_NO_FOLLOW) && !player->isCt() && toFollow != player) {
		player->print("%M does not welcome followers.\n", toFollow);
		return(0);
	}

	if(toFollow->isRefusing(player->name)) {
		player->print("%M doesn't allow you to follow %s right now.\n",
		      toFollow, toFollow->himHer());
		return(0);
	}

	if(toFollow->following && toFollow != player) {
		player->print("You must follow %s to group with %s right now.\n",
		      toFollow->following->name, toFollow->name);
		return(0);
	}

	if(toFollow->isGagging(player->name)) {
		player->print("You start following %s.\n", toFollow->name);
		return(0);
	}

	if(toFollow->following == player) {
		player->print("You can't. %s's following you.\n",
		      toFollow->upHeShe());
		return(0);
	}
	if(toFollow->flagIsSet(P_MISTED) && !player->isCt()) {
		player->print("How can you follow a mist?\n");
		return(0);
	}



	if(player->flagIsSet(P_NO_FOLLOW)) {
		player->print("You welcome followers again.\n");
		player->clearFlag(P_NO_FOLLOW);
	}
	if(player->following)
		doStopFollowing(player, TRUE);

	if(player == toFollow)
		return(0);

	addFollower(toFollow, player, TRUE);

	return(0);

}

int Creature::numFollowers() {
	int num=0;
	ctag *cp = first_fol;
	while(cp) {
		num += 1 + cp->crt->numFollowers();
		cp = cp->next_tag;
	}
	return(num);
}

// This function will make follower follow pCreature, pCreature must be a player
// follower must NOT be following anyone else
int addFollower(Creature * pCreature, Creature *follower, int notify) {
	ctag	*pp=0, *cp=0, *last=0, *cpnext=0;
	Player* creature = pCreature->getPlayer(), *pFollower = follower->getPlayer();

	ASSERTLOG( creature );
	ASSERTLOG( follower->following == NULL );

	follower->following = creature;

	pp = new ctag;
	if(!pp)
		merror("follow", FATAL);

	pp->crt = follower;
	pp->next_tag = 0;

	// creature = leader
	// add 2: 1 for leader, 1 for new follower
	int numFollowers = creature->numFollowers() + 2;

	creature->statistics.group(numFollowers);
	if(pFollower)
		pFollower->statistics.group(numFollowers);

	if(!creature->first_fol) {
		creature->first_fol = pp;
	} else {
		// Add monsters to the start of the follow list, players to the end
		if(!pFollower) {
			pp->next_tag = creature->first_fol;
			creature->first_fol = pp;

			// skip over the creature we just added
			cp = pp->next_tag;
			while(cp) {
				if(cp->crt->isPlayer())
					cp->crt->getPlayer()->statistics.group(numFollowers);
				cp = cp->next_tag;
			}
		} else {
			cp = creature->first_fol;
			while(cp) {
				if(cp->crt->isPlayer())
					cp->crt->getPlayer()->statistics.group(numFollowers);
				last = cp;
				cp = cp->next_tag;
			}
			last->next_tag = pp;
		}
	}

	if(pFollower) {
		pFollower->unmist();


		pFollower->print("You start following %s.\n", creature->name);

		if(!isDm(creature) && isStaff(creature))
			log_immort(true, creature, "%s follows %s in room %s.\n", pFollower->name, creature->name,
				pFollower->getRoom()->fullName().c_str());
		else if(!isDm(pFollower))
			log_immort(true, pFollower, "%s follows %s in room %s.\n", pFollower->name, creature->name,
				pFollower->getRoom()->fullName().c_str());


		if(!pFollower->flagIsSet(P_DM_INVIS) && !pFollower->flagIsSet(P_INCOGNITO) && !creature->isGagging(follower->name)) {
			creature->print("%M starts following you.\n", pFollower);
			broadcast(pFollower->getSock(), creature->getSock(), pFollower->getRoom(),
				"%M follows %N.", pFollower, creature);
		}
		cp 	= pFollower->first_fol;
		while(cp) {
			cpnext = cp->next_tag; // No Guarantee cp will still be valid if we call
								   // doStopFollowing
			if(cp->crt->isPlayer()) {
				if(!cp->crt->flagIsSet(P_DM_INVIS) && !cp->crt->flagIsSet(P_INCOGNITO))
					follower->print("%s must also follow %s now to follow you.\n", cp->crt->name, creature->name);
				cp->crt->print("You must follow %s now to follow %s.\n", creature->name, follower->name);
				cp->crt->print("You stop following %s.\n", follower->name);
				doStopFollowing(cp->crt, FALSE);
			}
			cp = cpnext;
		}
	}
	return(0);
}

// Causes the passed creature to stop following whoever they are
// following, and if notify is TRUE, notify that person if they can
// can see the person
int doStopFollowing(Creature *target, int notify) {
	ctag		*cp, *prev;
	Creature * following; // The person they are following
	Player* pTarget = target->getPlayer(), *pFollowing=0;

	if(target->following == NULL)
		return(0);

	following = target->following;
	cp = following->first_fol;

	if(cp->crt == target) {
		// They are the first person following them, so get rid of them
		following->first_fol = cp->next_tag;
		delete cp;
	} else {
		while(cp) {
			// Go through and find them
			if(cp->crt == target) {
				prev->next_tag = cp->next_tag;
				delete cp;
				break;
			}
			prev = cp;
			cp = cp->next_tag;
		}
	}
	target->following = 0;

	if(pTarget && notify == TRUE) {
		pTarget->print("You stop following %s.\n", following->name);
		pFollowing = following->getPlayer();

		if(!pTarget->flagIsSet(P_DM_INVIS) && !pTarget->flagIsSet(P_INCOGNITO) && pFollowing && !pFollowing->isGagging(pTarget->name))
			pFollowing->print("%M stops following you.\n", pTarget);
	}
	return(1);
}

// Causes pFollower to stop following pCreature
int doLose(Creature* crt, Creature* follower, int notify) {
	ASSERTLOG( crt != NULL );
	ASSERTLOG( follower != NULL );
	Player* pCreature = crt->getPlayer();
	Player *pFollower = follower->getPlayer();

	doStopFollowing(follower, FALSE);

	if(pCreature && pFollower && notify == TRUE) {
		pCreature->print("You lose %s.\n", pFollower->himHer());

		if(pFollower->isWatching(pCreature->name))
			pFollower->delWatching(pCreature->name);
		if(pCreature->isWatching(pFollower->name))
			pCreature->delWatching(pFollower->name);

		if(!pCreature->flagIsSet(P_DM_INVIS) && !pFollower->flagIsSet(P_INCOGNITO)) {
			pFollower->print("%M loses you.\n", pCreature);
			broadcast(pCreature->getSock(), pFollower->getSock(), pCreature->getRoom(), "%M loses %N.",
			               pCreature, pFollower);
		}
	}
	return(1);
}

//*********************************************************************
//						cmdLose
//*********************************************************************
// This function allows a player to lose another player who might be
// following them. When successful, that player will no longer be
// following.

int cmdLose(Player* player, cmd* cmnd) {
	Creature* target=0;


	player->clearFlag(P_AFK);

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

	if(cmnd->num == 1) {

		if(player->following == 0) {
			player->print("You're not following anyone.\n");
			return(0);
		}
		doStopFollowing(player, 1);
		return(0);
	}

	player->unhide();

	lowercize(cmnd->str[1], 1);
	//target = findCreature(player, player->first_fol, cmnd);
	ctag* cp = player->first_fol;
	int match = 0;
	while(cp) {
		if(!player->canSee(cp->crt)) {
			cp = cp->next_tag;
			continue;
		}
		if(keyTxtEqual(cp->crt, cmnd->str[1])) {
			match++;
			if(match == cmnd->val[1]) {
				target = cp->crt;
				break;
			}
		}
		cp = cp->next_tag;
	}
	if(!target) {
		player->print("That person is not following you.\n");
		return(0);
	}

	if(target->following != player) {
		player->print("That person is not following you.\n");
		return(0);
	}
	if(target->isPet()) {
		player->print("You can't lose your own pet!\n");
		return(0);
	}

	doLose(player, target, TRUE);

	return(0);

}

//
// this function is responsible for printing out a line of group member info
//
bstring groupLine(Creature* player, Creature* target) {
	bool	isPet = target->isPet();
	std::ostringstream oStr;

	if(!player->flagIsSet(P_NO_EXTRA_COLOR) && player->isEffected("know-aura"))
		oStr << target->alignColor();

	oStr << "  ";
	if(isPet)
		oStr << target->following->name << "'s " << target->name;
	else
		oStr << target->name;
	oStr << "^x";

	if(	player->isCt() ||
		(isPet && !target->following->flagIsSet(P_NO_SHOW_STATS)) ||
		(!isPet && !target->flagIsSet(P_NO_SHOW_STATS)) ||
		(isPet && target->following == player) ||
		(!isPet && target == player)
	) {
		oStr << " - " << (target->hp.getCur() < target->hp.getMax() && !player->flagIsSet(P_NO_EXTRA_COLOR) ? "^R" : "")
			 << std::setw(3) << target->hp.getCur() << "^x/" << std::setw(3) << target->hp.getMax()
			 << " Hp - " << std::setw(3) << target->mp.getCur() << "/" << std::setw(3)
			 << target->mp.getMax() << " Mp";

		if(!isPet) {
			if(target->isEffected("blindness"))
				oStr << ", Blind";
			if(target->isEffected("drunkenness"))
				oStr << ", Drunk";
			if(target->isEffected("confusion"))
				oStr << ", Confused";
			if(target->isDiseased())
				oStr << ", Diseased";
			if(target->isEffected("petrification"))
				oStr << ", Petrified";
			if(target->isPoisoned())
				oStr << ", Poisoned";
			if(target->isEffected("silence"))
				oStr << ", Silenced";
			if(target->flagIsSet(P_SLEEPING))
				oStr << ", Sleeping";
			else if(target->flagIsSet(P_UNCONSCIOUS))
				oStr << ", Unconscious";
			if(target->isEffected("wounded"))
				oStr << ", Wounded";
		}

		oStr << ".";
	}
	oStr << "\n";
	return(oStr.str());
}


//*********************************************************************
//						showGroupMembers
//*********************************************************************

bstring showGroupMembers(Player* player, ctag* cp, int *num) {
	Creature* follower=0;
	std::ostringstream oStr;
	while(cp) {
		follower = cp->crt;
		cp = cp->next_tag;

		// we assume that, if they're not a player, they're a pet
		// and are following someone
		// sending true to canSee skips invis/mist checks
		if(!player->canSee(follower->isPlayer() ? follower : follower->following, true))
			continue;

		// print out the follower
		if(follower != player) {
			(*num)++;
			oStr << groupLine(player, follower);
		}

		if(follower->first_fol)
			oStr << showGroupMembers(player, follower->first_fol, num);

	}
	return(oStr.str());
}

//*********************************************************************
//						cmdGroup
//*********************************************************************
// This function allows you to see who is in a group or party of people
// who are following you.

int cmdGroup(Player* player, cmd* cmnd) {
	Creature *leader=0;
	int num=0;
	std::ostringstream oStr;

	player->clearFlag(P_AFK);

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

	if(player->following)
		leader = player->following;
	else
		leader = player;

	oStr << "People in your party:\n";

	// you can always see who you're following
	if(player != leader) {
		// print out the leader
		num++;
		oStr << groupLine(player, leader);
	}

	oStr << showGroupMembers(player, leader->first_fol, &num);

	if(!num)
		oStr << "  No one but you.\n";
	player->printColor("%s\n", oStr.str().c_str());
	return(0);
}