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/
/*
 * Group.h
 *   Source file for 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 "group.h"

// C++ Includes
#include <iomanip>

//################################################################################
//# Group methods for groups
//################################################################################

Group::Group(Creature* pLeader) {
    //if(pLeader.inGroup())
    //  throw(std::runtime_error("Error: Leader already in another group\n"));
    flags = 0;
    add(pLeader);
    leader = pLeader;
    groupType = GROUP_DEFAULT;
    if(pLeader->pFlagIsSet(P_XP_DIVIDE))
        setFlag(GROUP_SPLIT_EXPERIENCE);
    if(pLeader->pFlagIsSet(P_GOLD_SPLIT))
        setFlag(GROUP_SPLIT_GOLD);

    name = bstring(leader->getName()) + "'s group";
    description = "A group, lead by " + bstring(leader->getName());
    // Register us in the server's list of groups
    gServer->registerGroup(this);
}

Group::~Group() {
    // Unregister us from the server's list of groups
    removeAll();
    gServer->unRegisterGroup(this);
}


//********************************************************************************
//* Add
//********************************************************************************
// Adds a creature to the group, and adds his pets too if the addPets parameter
// is set

bool Group::add(Creature* newMember, bool addPets) {
    Group* oldGroup = newMember->getGroup(false);
    if(oldGroup && oldGroup != this) {
        oldGroup->remove(newMember);
        oldGroup = NULL;
    }

    // No adding someone twice
    if(!oldGroup) {
        newMember->setGroup(this);
        members.push_back(newMember);
    }
    if(addPets) {
        for(Monster* mons : newMember->pets) {
            add(mons);
        }
    }
    newMember->setGroupStatus(GROUP_MEMBER);
    return(true);
}

//********************************************************************************
//* remove
//********************************************************************************
// Removes a creature from the group, adjusts leadership if necessary
// and disbands the group if necessary
//
// Returns: true  - The group was deleted and is no longer valid
//          false - The group still exists
bool Group::remove(Creature* toRemove) {
    CreatureList::iterator it = std::find(members.begin(), members.end(), toRemove);
    if(it != members.end()) {
        toRemove->setGroup(NULL);
        toRemove->setGroupStatus(GROUP_NO_STATUS);

        // Iterator is invalid now, do not try to access it after this
        members.erase(it);
        // Remove any pets this player had in the group
        for(Monster* mons : toRemove->pets) {
            if(remove(mons))
                return(true);
        }

        // See if the group should be disbanded
        if(this->getSize(false, false) <= 1)
        	return(disband());

        // We've already checked for a disband, now check for a leadership change
        if(toRemove == leader) {
        	leader = this->getMember(1, false);

        	// Something's wrong here
        	if(!leader) {
        		std::cout << "Couldn't find a replacement leader.\n";
        		return(disband());
        	}

        	leader->setGroupStatus(GROUP_LEADER);
            leader->print("You are now the group leader.\n");
            sendToAll(bstring(leader->getName()) + " is now the group leader.\n", leader);
        }


    }
    return(false);
}

//********************************************************************************
//* disband
//********************************************************************************
// Removes all players from a group and deletes it
bool Group::disband() {
    sendToAll("Your group has been disbanded.\n");
    removeAll();
    delete this;
    return(true);
}

//********************************************************************************
//* remove
//********************************************************************************
// Removes all players from a group (but does not delete it)
void Group::removeAll() {
    for(Creature* crt : members) {
        crt->setGroup(NULL);
        crt->setGroupStatus(GROUP_NO_STATUS);
    }
    members.clear();
}

//********************************************************************************
//* size
//********************************************************************************
// Returns the absolute size of the group
int Group::size() {
    int count=0;
    for(Creature* crt : members) {
        if(crt->getGroupStatus() >= GROUP_MEMBER)
            count++;
    }
    return(count);

}

//********************************************************************************
//* getSize
//********************************************************************************
// Parameters: countDmInvis - Should we count DM invis players or not?
//             membersOnly  - Should we count only members, or include invited people as well
// Returns: The number of players in the group
int Group::getSize(bool countDmInvis, bool membersOnly) {
	int count=0;
	for(Creature* crt : members) {
		if((countDmInvis || !crt->pFlagIsSet(P_DM_INVIS)) && crt->isPlayer() && (crt->getGroupStatus() >= GROUP_MEMBER || ! membersOnly))
			count++;
	}
	return(count);

}
//********************************************************************************
//* getNumInSameRoup
//********************************************************************************
// Returns the number of group members in the same room as the target
int Group::getNumInSameRoom(Creature* target) {
	int count=0;
	for(Creature* crt : members) {
		if(crt != target && target->inSameRoom(crt))
			count++;
	}
	return(count);
}
//********************************************************************************
//* getNumPlyInSameRoup
//********************************************************************************
// Returns the number of group members (players) in the same room as the target

int Group::getNumPlyInSameRoom(Creature* target) {
	int count=0;
	for(Creature* crt : members) {
		if(crt != target && crt->isPlayer() && target->inSameRoom(crt))
			count++;
	}
	return(count);
}

//********************************************************************************
//* getMember
//********************************************************************************
// Parameters: countDmInvis - Should we count DM invis players or not?
// Returns: The chosen players in the group

Creature* Group::getMember(int num, bool countDmInvis) {
    int count=0;
    for(Creature* crt : members) {
        if((countDmInvis || !crt->pFlagIsSet(P_DM_INVIS)) && crt->isPlayer() && crt->getGroupStatus() >= GROUP_MEMBER)
            count++;
        if(count == num)
            return(crt);
    }
    return(NULL);
}
//********************************************************************************
//* getMember
//********************************************************************************
// Parameters: 	name 		- Name (possibly partial) of the match we're looking for
//			   	num			- Number in the list for a match
//				Searcher	- The creature doing the search (allows nulls)
//				includePets - Include pets in the search
// Returns: A pointer to the creature, if found
Creature* Group::getMember(bstring name, int num, Creature* searcher, bool includePets) {
	int match = 0;
	for(Creature* crt : members) {
		if(!crt->isPlayer() && !includePets) continue;
		if(crt->getGroupStatus() < GROUP_MEMBER) continue;
		if(!searcher || !searcher->canSee(crt)) continue;
		if(keyTxtEqual(crt, name.c_str())) {
			if(++match == num) {
				return(crt);
			}
		}
	}
	return(NULL);
}


//********************************************************************************
//* inGroup
//********************************************************************************
// Returns: Is the target in this group?
bool Group::inGroup(Creature* target) {
    if(std::find(members.begin(), members.end(), target) == members.end())
        return(false);
    else
        return(true);
}

//********************************************************************************
//* SendToAll
//********************************************************************************
// Parameters: sendToInvited - Are invited members counted as in the group or not?
// Send msg to everyone in the group except ignore
void Group::sendToAll(bstring msg, Creature* ignore, bool sendToInvited) {
    for(Creature* crt : members) {
        if(!crt->isPet() && crt != ignore && (sendToInvited || crt->getGroupStatus() >= GROUP_MEMBER )) {
            *crt << ColorOn << msg << ColorOff;
        }
    }
}

//********************************************************************************
//* setGroupType
//********************************************************************************
void Group::setGroupType(GroupType newType) {
    groupType = newType;
}

//********************************************************************************
//* setName
//********************************************************************************
void Group::setName(bstring newName) {
    // Test validity of name here
    name = newName;
}

//********************************************************************************
//* setLeader
//********************************************************************************
bool Group::setLeader(Creature* newLeader) {
    //if(newLeader->getGroup() != this)
    //  return(false);
    leader->setGroupStatus(GROUP_MEMBER);
    newLeader->setGroupStatus(GROUP_LEADER);
    leader = newLeader;

    return(true);
}

//********************************************************************************
//* setDescription
//********************************************************************************
void Group::setDescription(bstring newDescription) {
    // Test validity of description here
    description = newDescription;
}

//********************************************************************************
//* getGroupType
//********************************************************************************
GroupType Group::getGroupType() {
    return(groupType);
}

//********************************************************************************
//* getLeader
//********************************************************************************
Creature* Group::getLeader() {
    return(leader);
}
//********************************************************************************
//* getName
//********************************************************************************
bstring& Group::getName() {
    return(name);
}
//********************************************************************************
//* getDescription
//********************************************************************************
bstring& Group::getDescription() {
    return(description);
}



//********************************************************************************
//* GetGroup
//********************************************************************************
// Parameter: bool inGroup - true - only return the group if they're at least a member
//                           false - return the group if they're an invitee as well
Group* Creature::getGroup(bool inGroup) {
    if(inGroup && groupStatus < GROUP_MEMBER)
        return(NULL);

    return(group);
}
//********************************************************************************
//* SetGroup
//********************************************************************************
void Creature::setGroup(Group* newGroup) {
    // Remove from existing group (Shouldn't happen)
    if(group && newGroup != NULL) {
        std::cout << "Setting group for " << getName() << " but they already have a group." << std::endl;
    }

    group = newGroup;
}
//********************************************************************************
//* SetGroupStatus
//********************************************************************************
void Creature::setGroupStatus(GroupStatus newStatus) {
    groupStatus = newStatus;
}

//********************************************************************************
//* GetGroupStatus
//********************************************************************************
GroupStatus Creature::getGroupStatus() {
    return(groupStatus);
}

//********************************************************************************
//* InSameGroup
//********************************************************************************
bool Creature::inSameGroup(Creature* target) {
	if(!target) return(false);
	return(getGroup() == target->getGroup());
}
//********************************************************************************
//* GetGroupLeader
//********************************************************************************
Creature* Creature::getGroupLeader() {
	group = getGroup();
	if(!group) return(NULL);
	return(group->getLeader());
}

//********************************************************************************
//* Group Flags
//********************************************************************************
void Group::setFlag(int flag) {
    if(flag <= GROUP_NO_FLAG || flag >= GROUP_MAX_FLAG)
        return;
    flags |= (1 << flag);
}
void Group::clearFlag(int flag) {
    if(flag <= GROUP_NO_FLAG || flag >= GROUP_MAX_FLAG)
        return;
    flags &= ~(1 << flag);
}

bool Group::flagIsSet(int flag) {
    return(flags & (1 << flag));
}

//################################################################################
//# Server methods for groups
//################################################################################

bool Server::registerGroup(Group* toRegister) {
    std::cout << "Registering " << toRegister->getName() << std::endl;
    groups.push_back(toRegister);
    return(true);
}

bool Server::unRegisterGroup(Group* toUnRegister) {
    std::cout << "Unregistering " << toUnRegister->getName() << std::endl;
    groups.remove(toUnRegister);
    return(true);
}


//################################################################################
//# Stream Operators and toString methods
//################################################################################

//********************************************************************************
//* GetGroupTypeStr
//********************************************************************************

bstring Group::getGroupTypeStr() {
    switch(getGroupType()) {
        case GROUP_PUBLIC:
        default:
            return("(Public)");
            break;
        case GROUP_INVITE_ONLY:
            return("(Invite Only)");
            break;
        case GROUP_PRIVATE:
            return("(Private)");
            break;
    }
    return("**Unknown**");
}
//********************************************************************************
//* GetGroupTypeStr
//********************************************************************************
bstring displayPref(bstring name, bool set) {
    return(name + (set ? "^gon^x" : "^roff^x"));
}
bstring Group::getFlagsDisplay() {
    std::ostringstream oStr;
    oStr << displayPref("Group Experience Split: ", flagIsSet(GROUP_SPLIT_EXPERIENCE));
    oStr << ", ";
    oStr << displayPref("Split Gold: ", flagIsSet(GROUP_SPLIT_GOLD));
    oStr << ".";
    return(oStr.str());
}

//********************************************************************************
//* GetGroupList
//********************************************************************************
// Returns the group listing used for displaying to staff
bstring Server::getGroupList() {
    std::ostringstream oStr;
    int i=1;
    for(Group* group : groups) {
        oStr << i++ << ") " << group->getName() << " " << group->getGroupTypeStr() <<  std::endl << group;
    }
    return(oStr.str());
}
//********************************************************************************
//* GetGroupTypeStr
//********************************************************************************
// Returns the group listing used for displaying to group members
bstring Group::getGroupList(Creature* viewer) {
    int i = 0;
    std::ostringstream oStr;

    for(Creature* target : members) {
        if(!viewer->isStaff() && (target->pFlagIsSet(P_DM_INVIS) || (target->isEffected("incognito") && !viewer->inSameRoom(target))))
            continue;
        bool isPet = target->isPet();
        oStr << ++i << ") ";
        if(!viewer->pFlagIsSet(P_NO_EXTRA_COLOR) && viewer->isEffected("know-aura") && target->getGroupStatus() != GROUP_INVITED)
            oStr << target->alignColor();

        if(isPet)
            oStr << target->getMaster()->getName() << "'s " << target->getName();
        else
            oStr << target->getName();
        oStr << "^x";
        if(target == leader) {
            oStr << " (Leader)";
        } else if(target->getGroupStatus() == GROUP_INVITED) {
            oStr << " (Invited).\n";
            continue;
        }
        if( viewer->isCt() ||
                (isPet && !target->getMaster()->flagIsSet(P_NO_SHOW_STATS)) ||
                (!isPet && !target->pFlagIsSet(P_NO_SHOW_STATS)) ||
                (isPet && target->getMaster() == viewer) ||
                (!isPet && target == viewer))
        {
            oStr << " - " << (target->hp.getCur() < target->hp.getMax() && !viewer->pFlagIsSet(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());
}

std::ostream& operator<<(std::ostream& out, const Group* group) {
    if(group)
        out << (*group);
    return(out);
}
std::ostream& operator<<(std::ostream& out, const Group& group) {
    int i = 0;
    for(Creature* crt : group.members) {
        out << "\t" << ++i << ") " << crt->getName();
        if(crt->getGroupStatus() == GROUP_LEADER)
            out << " (Leader)";
        else if(crt->getGroupStatus() == GROUP_INVITED)
            out << " (Invited)";
        out << std::endl;
    }
    return(out);
}