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