roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * 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-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */

// Mud includes
#include "mud.h"
#include "move.h"
#include "commands.h"

// C++ Includes
#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 << "Follow whom?\n";
		return(0);
	}


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

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

	if(toFollow == player && !player->getGroup(false)) {
		*player << "You can't group with yourself.\n";
		return(0);
	}

	if(toFollow->flagIsSet(P_NO_FOLLOW) && !player->isCt() && toFollow != player) {
		*player << toFollow << " does not welcome group members.\n";
		return(0);
	}

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

	if(toFollow->isGagging(player->getName())) {
		*player << "You start following " << toFollow->getName() << ".\n";
		return(0);
	}

	if(toFollow->isEffected("mist") && !player->isCt()) {
        player->print("How can you group with a mist?\n");
        return(0);
    }


	Group* toJoin = toFollow->getGroup(false);
	if(toJoin && player->getGroupStatus() != GROUP_INVITED) {
        // Check if in same group
        if(toJoin == player->getGroup() ) {
            player->print("You can't. %s is in the same group as you!\n", toFollow->upHeShe());
            return(0);
        }
        if(toJoin->getGroupType() != GROUP_PUBLIC) {
            player->print("%s's group is invite only.\n", toFollow->getCName());
            return(0);
        }
	}

	if(player->flagIsSet(P_NO_FOLLOW)) {
		player->print("You welcome group members again.\n");
		player->clearFlag(P_NO_FOLLOW);
	}
	if((toJoin && player->getGroupStatus() != GROUP_INVITED) || player == toFollow)
	    player->removeFromGroup(true);

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

	if(toJoin)
	    player->addToGroup(toJoin);
	else
	    toFollow->createGroup(player);



	return(0);

}
//********************************************************************************
//* AddToGroup
//********************************************************************************
// Adds the creature to the group, and announces if requested
void Creature::addToGroup(Group* toJoin, bool announce) {
    toJoin->add(this);
    if(announce) {
    	*this << ColorOn << "^gYou join \"" << toJoin->getName() << "\".\n" << ColorOff;
        toJoin->sendToAll(bstring("^g") + getName() + " has joined your group.\n", this);
        broadcast(getSock(), getRoomParent(), "%M joins the group \"%s\".", this, toJoin->getName().c_str());
    }

}
//********************************************************************************
//* CreateGroup
//********************************************************************************
// Creates a new group with the creature as the leader, and crt as a member
void Creature::createGroup(Creature* crt) {
    this->print("You have formed a group.\n");

    // Note: Group leader is added to this group in the constructor
    group = new Group(this);
    groupStatus = GROUP_LEADER;

    crt->addToGroup(group);
}

//*********************************************************************
//						RemoveFromGroup
//*********************************************************************
// Removes the creature and all of their pets from the group

bool Creature::removeFromGroup(bool announce) {
    if(group) {
        if(groupStatus == GROUP_INVITED) {
        	if(announce) {
        		if(!pFlagIsSet(P_DM_INVIS) && !isEffected("incognito"))
        			group->sendToAll(getCrtStr(NULL, CAP) + " rejects the invitation to join your group.\n");
            	*this << ColorOn << "^gYou reject the invitation to join \"" << group->getName() << "\".\n^x" << ColorOff;
        	}
            group->remove(this);
            group = null;
        } else {
        	if(announce) {
        		if(!pFlagIsSet(P_DM_INVIS) && !isEffected("incognito"))
        			group->sendToAll(getCrtStr(NULL, CAP) + " leaves the group.\n", this);
        		if(group->getLeader() == this)
        			*this << ColorOn << "^gYou leave your group.^x\n" << ColorOff;
        		else
        			*this << ColorOn << "^gYou leave \"" << group->getName() << "\".\n^x" << ColorOff;
        	}
            group->remove(this);
            group = null;
        }
        groupStatus = GROUP_NO_STATUS;
        return(true);
    }

    return(false);
}

//*********************************************************************
//						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->getGroup(false)) {
			*player << "You're not in a group.\n";
			return(0);
		}
		player->removeFromGroup(true);
		return(0);
	}

	player->unhide();

	Group* group = player->getGroup(true);

	if(!group) {
	    *player << "You are not in a group.\n";
	    return(0);
	}
	if(player != group->getLeader()) {
	    *player << "You are not the group leader.\n";
	    return(0);
	}

	lowercize(cmnd->str[1], 1);
	target = group->getMember(cmnd->str[1], cmnd->val[1], player, false);

	if(!target) {
		*player << "That person is not following you.\n";
		return(0);
	}

	player->removeFromGroup(true);

	return(0);

}


int printGroupSyntax(Player* player) {
    player->printColor("Syntax: group ^e<^xleave^e>\n");
    if(player->getGroupStatus() == GROUP_INVITED) {
    	player->printColor("              ^e<^xreject^e>\n");
    	player->printColor("              ^e<^xaccept^e>\n");
    }
    if(player->getGroupStatus() == GROUP_LEADER) {
        player->printColor("              ^e<^xpromote^e>^x ^e<^cplayer name^e>^x\n");
		player->printColor("              ^e<^xkick^e>^x ^e<^cplayer name^e>\n");
		player->printColor("              ^e<^xname^e>^x ^e<^cgroup name^e>\n");
		player->printColor("              ^e<^xtype^e>^x ^e<^cpublic/private/invite only^e>\n");
		player->printColor("              ^e<^xset^e>^x ^e<^csplit/xpsplit^e>\n");
		player->printColor("              ^e<^xclear^e>^x ^e<^csplit/xpsplit^e>\n");
		player->printColor("              ^e<^xdisband^e>^x\n");
    }
    if(player->getGroupStatus() == GROUP_LEADER
    		|| (player->getGroupStatus() == GROUP_MEMBER && player->getGroup(true) && player->getGroup(true)->getGroupType() < GROUP_PRIVATE) )
    {
		player->printColor("              ^e<^xinvite^e>^x ^e<^cplayer name^e>\n");
    }
    return(0);
}

//*********************************************************************
//						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) {
	player->clearFlag(P_AFK);

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


	if(cmnd->num >= 2) {
	    int len = strlen(cmnd->str[1]);

	    if(!strncasecmp(cmnd->str[1], "invite", len))       return(Group::invite(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "accept", len) || !strncasecmp(cmnd->str[1], "join", len))     return(Group::join(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "leave", len)  || !strncasecmp(cmnd->str[1], "reject",len))    return(Group::leave(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "disband", len)) return(Group::disband(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "kick", len))    return(Group::kick(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "promote", len)) return(Group::promote(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "name", len)) 	return(Group::rename(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "type", len)) 	return(Group::type(player, cmnd));
	    else if(!strncasecmp(cmnd->str[1], "set", len))     return(Group::set(player, cmnd, true));
	    else if(!strncasecmp(cmnd->str[1], "clear", len))   return(Group::set(player, cmnd, false));
	    else return(printGroupSyntax(player));
	}

	Group* group = player->getGroup(false);
    if(!group) {
        *player << "You are not in a group.\n";
        return(0);
    }
    if(player->getGroupStatus() == GROUP_INVITED) {
        *player << "You have been invited to join \"" << group->getName() << "\".\nTo accept, type <group accept>; To reject type <group reject>.\n";
        return(0);
    }
    *player << group->getName() << " " << group->getGroupTypeStr() << ":\n";
    *player << ColorOn << group->getFlagsDisplay() << "\n" << ColorOff;

    *player << ColorOn << group->getGroupList(player) << ColorOff;

	return(0);
}

int Group::invite(Player* player, cmd* cmnd) {
    Player* target = 0;

    if(cmnd->num < 3) {
        *player << "Invite who into your group?\n";
        return(0);
    }

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

    if(!target || !player->canSee(target) || target == player) {
        *player << "That player is not logged on.\n";
        return(0);
    }

    if(Move::tooFarAway(player, target, "invite to a group"))
        return(0);

    if(target->getGroup(false)) {
        if(target->getGroupStatus() == GROUP_INVITED) {
            if(target->getGroup(false) == player->getGroup(false))
                *player << target << " is already considering joining your group.\n";
            else
                *player << target << " is already considering joining another group.\n";
        }
        else
            *player << target << " is already in another group.\n";
        return(0);
    }


    Group* group = player->getGroup(false);
    if(group) {
    	if(group->getGroupType() == GROUP_PRIVATE && player->getGroupStatus() != GROUP_LEADER) {
    		*player << "You are not the group leader of \"" << group->getName() << "\".\n";
    		return(0);
    	}
    	if(player->getGroupStatus() < GROUP_MEMBER) {
    		*player << "Reject your current group invitation before you try to start a group!\n";
    		return(0);
    	}
    }

    if(!group) {
        group = new Group(player);
        player->setGroupStatus(GROUP_LEADER);
    }

    group->add(target, false);
    target->setGroupStatus(GROUP_INVITED);

    *player << ColorOn << "^gYou invite " << target << " to join your group.\n" << ColorOff;
    *target << ColorOn << "^g" << player << " invites you to join \"" << group->getName() << "\".\n" << ColorOff;

    return(0);
}

// Accept an invitation and join a group
int Group::join(Player* player, cmd *cmnd)  {
	if(player->getGroupStatus() != GROUP_INVITED) {
		*player << "You have not been invited to join any groups.\n";
		return(0);
	}
	Group* toJoin = player->getGroup(false);
	if(!toJoin) {
		// Shouldn't happen
		*player << "You have not been invited to join any groups.\n";
		return(0);
	}
	player->addToGroup(toJoin, true);
    return(0);
}
int Group::reject(Player* player, cmd* cmnd) {
	if(player->getGroupStatus() != GROUP_INVITED) {
		*player << "You have not been invited to join any groups.\n";
		return(0);
	}
	Group* toReject = player->getGroup(false);
	if(!toReject) {
		// Shouldn't happen
		*player << "You have not been invited to join any groups.\n";
		return(0);
	}
	player->removeFromGroup(true);
    return(0);
}
int Group::disband(Player* player, cmd* cmnd) {
	Group* toDisband = player->getGroup(true);
	if(!toDisband) {
		*player << "You are not in a group.\n";
		return(0);
	}
	if(player->getGroupStatus() != GROUP_LEADER) {
		*player << "You are not the group leader of \"" << toDisband->getName() << "\".\n";
		return(0);
	}
	*player << "You disband \"" << toDisband->getName() << "\".\n";
	toDisband->sendToAll(bstring(player->getName()) + " disbands the group.\n", player);
	toDisband->disband();

    return(0);
}
int Group::promote(Player* player, cmd* cmnd) {
	Group* group = player->getGroup(true);
	if(!group) {
		*player << "You are not in a group.\n";
		return(0);
	}
	if(player->getGroupStatus() != GROUP_LEADER) {
		*player << "You are not the group leader of \"" << group->getName() << "\".\n";
		return(0);
	}

    Player* target = 0;

    if(cmnd->num < 3) {
        *player << "Who would you like to promote to leader?\n";
        return(0);
    }

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

    if(!target || !player->canSee(target) || target == player) {

        *player << "That player is not logged on.\n";
        return(0);
    }

    if(target->getGroup(true) != group) {
    	*player << target->getName() << " is not in your group!\n";
    	return(0);
    }
    group->setLeader(target);

    *player << ColorOn << "^gYou promote " << target->getName() << " to group leader\nYou are now a member of \"" << group->getName() << "\".\n" << ColorOff;
	group->sendToAll(bstring("^g") + player->getName() + " promotes " + target->getName() + " to group leader.^x\n", player);

	*target << ColorOn << "^gYou are now the group leader of \"" << group->getName() << "\".\n^x" << ColorOff;


    return(0);
}
int Group::kick(Player* player, cmd* cmnd) {
	Group* group = player->getGroup(true);
	if(!group) {
		*player << "You are not in a group.\n";
		return(0);
	}
	if(group->getGroupType() == GROUP_PRIVATE && player->getGroupStatus() != GROUP_LEADER) {
		*player << "You are not the group leader of \"" << group->getName() << "\".\n";
		return(0);
	}

    Player* target = 0;

    if(cmnd->num < 3) {
        *player << "Who would you like to kick from your group?\n";
        return(0);
    }

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

    if(!target || !player->canSee(target) || target == player) {

        *player << "That player is not logged on.\n";
        return(0);
    }

    // We can also remove invitations from people by "kicking" them
    if(target->getGroup(false) != group) {
    	*player << target->getName() << " is not in your group!\n";
    	return(0);
    }
    if(target->getGroupStatus() == GROUP_INVITED) {
    	*player << "You rescind the group invitation from " << target->getName() << ".\n";
    	group->sendToAll(bstring(player->getName()) + " rescinds the invitation for " + target->getName() + " to join the group.\n", player);
    	*target << player << " rescinds your invitation to join \"" << group->getName() << "\".\n";
    	target->removeFromGroup(false);
    }
    else {
    	if(player->getGroupStatus() != GROUP_LEADER) {
    		*player << "You can't kick people out of the group!\n";
    		return(0);
    	} else {
			*player << ColorOn << "^gYou kick " << target->getName() << " out of your group.^x\n" << ColorOff;
			group->sendToAll(bstring("^g") + player->getName() + " kicks " + target->getName() + " out of the group.^x\n", player);
			target->removeFromGroup(true);
    	}
    }
    return(0);
}
int Group::leave(Player* player, cmd* cmnd) {
	if(!player->getGroup(false)) {
		*player << "You're not in a group.\n";
		return(0);
	}
	if(!strncmp(cmnd->str[1], "reject", strlen(cmnd->str[1])) && player->getGroupStatus() != GROUP_INVITED) {
	    *player << "You have no group invitations to reject.\n";
	    return(0);
	}
	player->removeFromGroup(true);

    return(0);
}
int Group::rename(Player* player, cmd* cmnd) {
	Group* group = player->getGroup(true);
	if(!group) {
		*player << "You are not in a group.\n";
		return(0);
	}
	if(player->getGroupStatus() != GROUP_LEADER) {
		*player << "You are not the group leader of \"" << group->getName() << "\".\n";
		return(0);
	}

	if(cmnd->num < 3) {
		*player << "What would you like to name your group?\n";
		return(0);
	}

	bstring newName = getFullstrText(cmnd->fullstr, 2);
	if(newName.empty()) {
		*player << "What would you like to name your group?\n";
		return(0);
	}
	group->setName(newName);
	*player << ColorOn << "^gYou rename your group to \"" << newName << "\".\n^x" << ColorOff;
	group->sendToAll(bstring("^g") + player->getName() + " renames the group to \"" + newName + "\".\n^x", player, true);

    return(0);
}
int Group::type(Player* player, cmd* cmnd) {
	Group* group = player->getGroup(true);
	const char *errorMsg = "What would you like to switch your group to? (Public, Private, Invite Only)?\n";
	if(!group) {
		*player << "You are not in a group.\n";
		return(0);
	}
	if(player->getGroupStatus() != GROUP_LEADER) {
		*player << "You are not the group leader of \"" << group->getName() << "\".\n";
		return(0);
	}

	if(cmnd->num < 3) {
		*player << errorMsg;
		return(0);
	}

	bstring newName = getFullstrText(cmnd->fullstr, 2);
	if(newName.empty()) {
	    *player << errorMsg;
		return(0);
	}
	int len = newName.length();
	const char *str = newName.c_str();
	if(len >= 2) {
		if(!strncasecmp(str, "public", len)) {
			group->setGroupType(GROUP_PUBLIC);
			*player << ColorOn << "^gYou change the group type to Public.\n" << ColorOff;
			group->sendToAll(bstring("^g") + player->getName() + " changes the group to Public.\n^x", player);
			return(0);
		} else if(!strncasecmp(str, "private", len)) {
			group->setGroupType(GROUP_PRIVATE);
			*player << ColorOn << "^gYou change the group type to Private.^x\n" << ColorOff;
			group->sendToAll(bstring("^g") + player->getName() + " changes the group to Private.^x\n", player);
			return(0);
		}
	}
	if(!strncasecmp(str, "invite only", len) || !strncmp(str, "inviteonly", len)) {
		group->setGroupType(GROUP_INVITE_ONLY);
		*player << ColorOn << "^gYou change the group type to Invite Only.^x\n" << ColorOff;
		group->sendToAll(bstring("^g") + player->getName() + " changes the group to Invite Only.\n^x", player);
		return(0);
	}
	*player << errorMsg;
    return(0);
}

int Group::set(Player* player, cmd* cmnd, bool set) {
    Group* group = player->getGroup(true);
    const char* errorMsg;
    if(set)
        errorMsg = "What group flag would you like to set? (Currently available: Split, XpSplit).\n";
    else
        errorMsg = "What group flag would you like to clear? (Currently available: Split, XpSplit).\n";

    if(!group) {
        *player << "You are not in a group.\n";
        return(0);
    }
    if(player->getGroupStatus() != GROUP_LEADER) {
        *player << "You are not the group leader of \"" << group->getName() << "\".\n";
        return(0);
    }

    if(cmnd->num < 3) {
        *player << errorMsg;
        return(0);
    }

    bstring newName = getFullstrText(cmnd->fullstr, 2);
    if(newName.empty()) {
        *player << errorMsg;
        return(0);
    }
    int len = newName.length();
    const char *str = newName.c_str();
    if(!strncasecmp(str, "split", len)) {
        if(set) {
            group->setFlag(GROUP_SPLIT_GOLD);
            group->sendToAll("^gGold will now be split with the group.^x\n");
        } else {
            group->clearFlag(GROUP_SPLIT_GOLD);
            group->sendToAll("^gGold will no longer be split with the group.^x\n");
        }
        return(0);
    } else if(!strncasecmp(str, "xpsplit", len)) {
        if(set) {
            group->setFlag(GROUP_SPLIT_EXPERIENCE);
            group->sendToAll("^gGroup experience split enabled.^x\n");
        } else {
            group->clearFlag(GROUP_SPLIT_EXPERIENCE);
            group->sendToAll("^gGroup experience split disabled.^x\n");
        }
        return(0);
    }

    *player << errorMsg;
    return(0);
}
//********************************************************************
//						doFollow
//********************************************************************

void Player::doFollow(BaseRoom* oldRoom) {
	Group* group = getGroup(true);
	if(getGroupStatus() == GROUP_LEADER && group) {
		for(Creature* crt : group->members) {
			if(crt->getRoomParent() == oldRoom) {
				Player* pFollow = crt->getAsPlayer();
				Monster* mFollow = crt->getAsMonster();
				if(pFollow) {
					pFollow->deleteFromRoom();
					pFollow->addToRoom(getRoomParent());
				} else {
					mFollow->deleteFromRoom();
					mFollow->addToRoom(getRoomParent());
				}
			}
		}
	}
	doPetFollow();
}

//********************************************************************
//						doPetFollow
//********************************************************************

void Player::doPetFollow() {
	for(Monster*pet : pets ){
		if(pet && pet->getRoomParent() != getRoomParent()) {
			pet->deleteFromRoom();
			pet->addToRoom(getRoomParent());
		}
		// TODO: Not sure if we need this check any more
		if(alias_crt && alias_crt->getRoomParent() != getRoomParent()) {
			alias_crt->deleteFromRoom();
			alias_crt->addToRoom(getRoomParent());
		}
	}
}

//*********************************************************************
//						getsGroupExperience
//*********************************************************************

bool Creature::getsGroupExperience(Monster* target) {
	// We can get exp if we're a player...
	return(isPlayer() &&
		// And idle less than 2 minutes
		getAsPlayer()->getIdle() < 120 &&
		// And we haven't abused group exp
		!flagIsSet(P_GROUP_EXP_ABUSE) &&
		// And we're not a DM invis person
		!flagIsSet(P_DM_INVIS) &&
		// And we are visible! (No more clerics/mages sitting invis leeching)
		!isInvisible() &&
		// No invis players
		!flagIsSet(P_HIDDEN) &&
		// No mists either
		!isEffected("mist") &&
		// no self-declared AFK people
		!flagIsSet(P_AFK) &&
		// no unconscious people
		!flagIsSet(P_UNCONSCIOUS) &&
		// no statues
		!isEffected("petrification") &&
		// and they're on the enemy list
		target->isEnemy(this)
	);
}