/*
* 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)
);
}