/*
* prefs.cpp
* Player preferences
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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
*
*/
#include "mud.h"
#include "commands.h"
#include "login.h"
#include "boost/format.hpp"
// having a pref that starts with a hyphen (-) is instead a category
typedef struct prefInfo {
bstring name;
int flag;
bool (*canUse)(const Creature *);
bstring desc;
bool invert; // reverses the language
} prefInfo, *prefPtr;
prefInfo prefList[] =
{
// name flag canUse desc
{ "-Staff Preferences", 0, isStaff, "", 0 },
{ "eaves", P_EAVESDROPPER, isCt, "eavesdropper mode", 0 },
{ "supereaves", P_SUPER_EAVESDROPPER, isCt, "super eavesdropper mode", 0 },
{ "msg", P_NO_MSG, isCt, "*msg channel", true },
{ "mobtick", P_NO_TICK_MSG, isCt, "See mob tick broadcasts", true },
{ "mobaggro", P_NO_AGGRO_MSG, isCt, "See mob aggro broadcasts", true },
{ "mobdeath", P_NO_DEATH_MSG, isCt, "See mob death broadcasts", true },
{ "ttobound", P_T_TO_BOUND, isStaff,"*t to bound room", 0 },
{ "wts", P_NO_WTS, isCt, "*wts channel", true },
{ "zones", P_VIEW_ZONES, isCt, "view overland zones", 0 },
{ "autoinvis", P_AUTO_INVIS, isCt, "auto-invis if idle for 100+ mins", 0 },
{ "allhooks", P_SEE_ALL_HOOKS, isDm, "see all hooks", 0 },
{ "hooks", P_SEE_HOOKS, isDm, "see triggered hooks", 0 },
{ "-Color", 0, 0, "", 0 },
{ "mirc", P_MIRC, 0, "mirc colors", 0 },
{ "ansi", P_ANSI_COLOR, 0, "ansi colors", 0 },
{ "langcolor", P_LANGUAGE_COLORS, 0, "language colors", 0 },
{ "extracolor", P_NO_EXTRA_COLOR, 0, "extra color options", true },
{ "areacolor", P_INVERT_AREA_COLOR, 0, "invert area colors", 0 },
{ "mxpcolors", P_MXP_ENABLED, 0, "use MXP for more than 16 colors", 0 },
{ "-Channels", 0, 0, "", 0 },
{ "cls", P_IGNORE_CLASS_SEND, 0, "class channel", true },
{ "race", P_IGNORE_RACE_SEND, 0, "race channel", true },
{ "broad", P_NO_BROADCASTS, 0, "broadcast channel", true },
{ "newbie", P_IGNORE_NEWBIE_SEND, 0, "newbie channel", true },
{ "gossip", P_IGNORE_GOSSIP, 0, "gossip channel", true },
{ "clan", P_IGNORE_CLAN, 0, "clan channel", true },
{ "tells", P_NO_TELLS, 0, "send/tell/whisper/sign", true },
{ "sms", P_IGNORE_SMS, 0, "receive text messages", true },
{ "ignore", 0, 0, "ignore all channels", 0 },
// do something with P_IGNORE_ALL
{ "-Notifications", 0, 0, "", 0 },
{ "duel", P_NO_DUEL_MESSAGES, 0, "duel messages", true },
{ "login", P_NO_LOGIN_MESSAGES, 0, "login messages", true },
{ "shopprofit", P_DONT_SHOW_SHOP_PROFITS,0, "shop profit notifications",true },
{ "mail", P_NO_SHOW_MAIL, 0, "mudmail notifications", true },
{ "permdeath", P_PERM_DEATH, 0, "perm death broadcasts", 0 },
{ "auction", P_NO_AUCTIONS, 0, "player auctions", true },
{ "showforum", P_HIDE_FORUM_POSTS, 0, "notification of forum posts", true },
{ "forumbrevity",P_FULL_FORUM_POSTS, 0, "forum posts notification length", true },
{ "notifications",0, 0, "show all notifications", 0 },
{ "-Combat", 0, 0, "", 0 },
{ "autoattack", P_NO_AUTO_ATTACK, 0, "auto attack", true },
{ "lagprotect", P_LAG_PROTECTION_SET, 0, "lag protection", 0 },
{ "lagrecall", P_NO_LAG_HAZY, 0, "recall potion if below half hp",true },
{ "wimpy", P_WIMPY, 0, "flee when HP below this number",0 },
{ "killaggros", P_KILL_AGGROS, 0, "attack aggros first", 0 },
{ "mobnums", P_NO_NUMBERS, 0, "monster ordinal numbers", true },
{ "autotarget", P_NO_AUTO_TARGET, 0, "automatic targeting", true },
{ "-Group", 0, 0, "", 0 },
{ "group", P_IGNORE_GROUP_BROADCAST,0, "group combat messages", true },
{ "xpsplit", P_XP_DIVIDE, 0, "group experience split", 0 },
{ "split", P_GOLD_SPLIT, 0, "split gold among group", 0 },
{ "stats", P_NO_SHOW_STATS, 0, "show group your stats", true },
{ "follow", P_NO_FOLLOW, 0, "can be followed", true },
{ "-Display", 0, 0, "", 0 },
{ "showall", P_SHOW_ALL_PREFS, 0, "show all preferences", false },
{ "short", P_NO_SHORT_DESCRIPTION, 0, "short description", true },
{ "long", P_NO_LONG_DESCRIPTION, 0, "long description", true },
{ "nlprompt", P_NEWLINE_AFTER_PROMPT, 0, "newline after prompt", 0 },
{ "xpprompt", P_SHOW_XP_IN_PROMPT, 0, "show exp in prompt", 0 },
{ "prompt", P_PROMPT, 0, "descriptive prompt", 0 },
{ "compact", P_COMPACT, 0, "compacts most output", false },
{ "tick", P_SHOW_TICK, 0, "shows ticks", false },
{ "skillprogress", P_SHOW_SKILL_PROGRESS,0, "show skill progress bar", false },
{ "-Miscellaneous", 0, 0, "", 0 },
{ "autowear", P_NO_AUTO_WEAR, 0, "wear all on login", true },
{ "summon", P_NO_SUMMON, 0, "can be summoned", true },
//{ "pkill", P_NO_PKILL_PERCENT, 0, "show pkill percentage", true },
{ "afk", P_AFK, 0, "away from keyboard", 0 },
{ "statistics", P_NO_TRACK_STATS, 0, "track statistics", true },
// handle wimpy below
// handle afk below
{ "", 0, 0, "" }
};
//*********************************************************************
// cmdTelOpts
//*********************************************************************
int cmdTelOpts(Player* player, cmd* cmnd) {
Socket* sock = player->getSock();
Player* target = player;
if(!sock)
return(0);
if(cmnd->num > 1) {
if(!strcasecmp(cmnd->str[1], "wrap")) {
player->print("Setting wrap to '%d'\n", player->setWrap(cmnd->val[1]));
return(0);
} else {
if(player->isCt()) {
cmnd->str[1][0] = up(cmnd->str[1][0]);
target = gServer->findPlayer(cmnd->str[1]);
if(target && (!target->isDm() || player->isDm())) {
sock = target->getSock();
if(!sock) {
player->print("No socket.\n");
return(0);
}
} else {
player->print("Unknown option.\n");
return(0);
}
} else {
player->print("Unknown option.\n");
return(0);
}
}
}
std::ostringstream oStr;
boost::format formatWithDesc("%1% (%2%) %|40t|%3%\n");
boost::format formatNoDesc("%1% ^c%|42t|%2%^x\n");
formatNoDesc.exceptions( boost::io::all_error_bits ^ ( boost::io::too_many_args_bit | boost::io::too_few_args_bit ) );
formatWithDesc.exceptions( boost::io::all_error_bits ^ ( boost::io::too_many_args_bit | boost::io::too_few_args_bit ) );
try {
if(target != player)
oStr << "^cNegotiated Telnet Options for " << target->getName() << "^x\n";
else
oStr << "^cNegotiated Telnet Options^x\n";
oStr << "-------------------------------------------------------\n";
oStr << formatNoDesc % "Term" % sock->getTermType();
oStr << formatNoDesc % "Term Size" % bstring(bstring(sock->getTermCols()) + " x " + bstring(sock->getTermRows()));
if(player->getWrap() == 0)
oStr << formatNoDesc % "Server Linewrap" % "None";
else if(player->getWrap() == -1)
oStr << formatNoDesc % "Server Linewrap" % (bstring("Using Cols (") + sock->getTermCols() + bstring(")"));
else
oStr << formatNoDesc % "Server Linewrap" % target->getWrap();
oStr << formatWithDesc % "MCCP" % "Mud Client Compression Protocol" % ((sock->getMccp() != 0) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "MXP" % "MUD eXtension Protocol" % ((sock->getMxp() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "MSDP" % "MUD Server Data Protocol" % ((sock->getMsdp() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "ATCP" % "Achaea Telnet Client Protocol" % ((sock->getAtcp() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "Charset" % "Charset Negotiation" % ((sock->getCharset() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "UTF-8" % "UTF-8 Support" % ((sock->getUtf8() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "MSP" % "Mud Sound Protocol" % ((sock->getMsp() == true) ? "^gon^x" : "^roff^x");
oStr << formatWithDesc % "EOR" % "End of Record" % ((sock->getEor() == true) ? "^gon^x" : "^roff^x");
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
player->printColor("%s", oStr.str().c_str());
//player->print(" %-13s :: %-31s :: ", prefList[i].name.c_str(), prefList[i].desc.c_str());
return(0);
}
//*********************************************************************
// cmdPrefs
//*********************************************************************
int cmdPrefs(Player* player, cmd* cmnd) {
prefPtr pref = NULL;
int i=0, match=0, len=strlen(cmnd->str[1]);
bool set = cmnd->str[0][0]=='s';
bool toggle = cmnd->str[0][0]=='t' || cmnd->str[0][0]=='p';
bool show=false;
bool all = !strcmp(cmnd->str[1], "-all") || (len == 0 && player->flagIsSet(P_SHOW_ALL_PREFS));
bstring prefName="";
if(!player->ableToDoCommand())
return(0);
// display a list of preferences
if(cmnd->num == 1 || cmnd->str[1][0] == '-') {
if(!len || !all) {
player->print("Below is a list of preference categories. To view a cateogry, type\n");
player->print("\"pref -[category]\". To view all preferences, type \"pref -all\".\n\n");
}
while(prefList[i].name != "") {
if(!prefList[i].canUse || prefList[i].canUse(player)) {
if(prefList[i].name.at(0) == '-') {
// previous category was shown? newline
if(show)
player->print("\n");
// no parameters? indent
if(!len)
player->print(" ");
prefName = prefList[i].name;
show = all || (len && !strncmp(cmnd->str[1], prefName.toLower().c_str(), len));
player->printColor("^%s%s^w\n", show ? "C" : "c", prefList[i].name.substr(1, prefList[i].name.length() - 1).c_str());
} else {
if(!show) {
i++;
continue;
}
player->print(" %-13s :: %-31s :: ", prefList[i].name.c_str(), prefList[i].desc.c_str());
// wimpy gets its own special format
if(prefList[i].name == "wimpy" && player->flagIsSet(prefList[i].flag))
player->print("%d", player->getWimpy());
else if(prefList[i].name == "forumbrevity")
player->print("%s", player->flagIsSet(prefList[i].flag) ? "full" : "brief");
// print everything exp ignore
else if(prefList[i].flag)
player->printColor("%s^w", player->flagIsSet(prefList[i].flag) ^ !prefList[i].invert ? "^roff" : "^gon");
player->print("\n");
}
}
i++;
}
player->print("\n");
return(0);
}
// everyone is used to nosummon - let them keep it that way
if(!strcmp(cmnd->str[1], "nosummon")) {
if(!toggle)
set = !set;
strcpy(cmnd->str[1], "summon");
}
// we're setting or clearing a preference
// find out which one
while(prefList[i].name != "") {
if( prefList[i].name.at(0) != '-' &&
(!prefList[i].canUse || prefList[i].canUse(player))
) {
if(!strcmp(cmnd->str[1], prefList[i].name.c_str())) {
match = 1;
pref = &prefList[i];
break;
} else if(!strncmp(cmnd->str[1], prefList[i].name.c_str(), len)) {
match++;
pref = &prefList[i];
}
}
i++;
}
if(match > 1) {
player->print("Preference not unique.\n");
return(0);
}
if(!pref) {
player->print("Unknown preference.\n");
return(0);
}
// if any channels are being ignored, toggle = stop ignorning
if(toggle && pref->name == "ignore") {
toggle = false;
set = (!player->flagIsSet(P_IGNORE_CLASS_SEND) &&
!player->flagIsSet(P_IGNORE_RACE_SEND) &&
!player->flagIsSet(P_NO_BROADCASTS) &&
!player->flagIsSet(P_IGNORE_NEWBIE_SEND) &&
!player->flagIsSet(P_IGNORE_GOSSIP) &&
!player->flagIsSet(P_IGNORE_CLAN) &&
!player->flagIsSet(P_NO_TELLS)
);
}
// if any notifications are off, toggle = turn them all on
if(toggle && pref->name == "notifications") {
toggle = false;
set = !(player->flagIsSet(P_NO_DUEL_MESSAGES) ||
!player->flagIsSet(P_NO_LOGIN_MESSAGES) ||
!player->flagIsSet(P_DONT_SHOW_SHOP_PROFITS) ||
!player->flagIsSet(P_NO_SHOW_MAIL) ||
!player->flagIsSet(P_NO_SHOW_MAIL) ||
!player->flagIsSet(P_IGNORE_GROUP_BROADCAST) ||
player->flagIsSet(P_PERM_DEATH) ||
!player->flagIsSet(P_NO_AUCTIONS) ||
!player->flagIsSet(P_HIDE_FORUM_POSTS)
);
}
// set
//---------------
//NORMAL
// toggle
// !flag
// toggle
// invert && flag
// are we setting or clearing?
if( ((set ^ pref->invert) && !toggle) ||
(toggle && !player->flagIsSet(pref->flag))
) {
if(pref->flag == P_MXP_ENABLED) {
if(!player->getSock()->getMxp()) {
player->print("Your client does not support MXP!\nYou will not be able to view extra colors.\n");
return(0);
}
player->getSock()->defineMxp();
player->getSock()->setColorOpt(MXP_COLOR);
}
if(pref->name == "ansi") {
if(player->flagIsSet(P_MXP_ENABLED))
player->getSock()->setColorOpt(MXP_COLOR);
else
player->getSock()->setColorOpt(ANSI_COLOR);
}
if(pref->name == "ignore") {
player->setFlag(P_IGNORE_CLASS_SEND);
player->setFlag(P_IGNORE_RACE_SEND);
player->setFlag(P_NO_BROADCASTS);
player->setFlag(P_IGNORE_NEWBIE_SEND);
player->setFlag(P_IGNORE_GOSSIP);
player->setFlag(P_IGNORE_CLAN);
player->setFlag(P_NO_TELLS);
} else if(pref->name == "notifications") {
player->clearFlag(P_NO_DUEL_MESSAGES);
player->clearFlag(P_NO_LOGIN_MESSAGES);
player->clearFlag(P_DONT_SHOW_SHOP_PROFITS);
player->clearFlag(P_NO_SHOW_MAIL);
player->clearFlag(P_IGNORE_GROUP_BROADCAST);
player->setFlag(P_PERM_DEATH);
player->clearFlag(P_NO_AUCTIONS);
player->clearFlag(P_HIDE_FORUM_POSTS);
} else
player->setFlag(pref->flag);
if(pref->name == "afk") {
// broadcast for going afk
if(!player->flagIsSet(P_DM_INVIS))
broadcast(player->getSock(), player->getParent(), "%M has gone afk.", player);
else
broadcast(isCt, player->getSock(), player->getRoomParent(), "*DM* %M has gone afk.", player);
}
if(pref->name == "wimpy") {
player->setWimpy(cmnd->val[1] == 1L ? 10 : cmnd->val[1]);
player->setWimpy(MAX(player->getWimpy(), 2));
}
set = true;
} else {
if(pref->name == "ansi") {
player->getSock()->setColorOpt(NO_COLOR);
}
if(pref->name == "ignore") {
player->clearFlag(P_IGNORE_CLASS_SEND);
player->clearFlag(P_IGNORE_RACE_SEND);
player->clearFlag(P_NO_BROADCASTS);
player->clearFlag(P_IGNORE_NEWBIE_SEND);
player->clearFlag(P_IGNORE_GOSSIP);
player->clearFlag(P_IGNORE_CLAN);
player->clearFlag(P_NO_TELLS);
} else if(pref->name == "notifications") {
player->setFlag(P_NO_DUEL_MESSAGES);
player->setFlag(P_NO_LOGIN_MESSAGES);
player->setFlag(P_DONT_SHOW_SHOP_PROFITS);
player->setFlag(P_NO_SHOW_MAIL);
player->setFlag(P_IGNORE_GROUP_BROADCAST);
player->clearFlag(P_PERM_DEATH);
player->setFlag(P_NO_AUCTIONS);
player->setFlag(P_HIDE_FORUM_POSTS);
} else
player->clearFlag(pref->flag);
if(pref->name == "wimpy")
player->setWimpy(0);
set = false;
}
// give them the message
player->print("Preference : %s : ", pref->desc.c_str());
if(pref->name == "wimpy" && player->flagIsSet(pref->flag))
player->print("%d", player->getWimpy());
else if(pref->name == "forumbrevity")
player->print("%s", player->flagIsSet(pref->flag) ? "full" : "brief");
else
player->printColor("%s^w", set ^ !pref->invert ? "^roff" : "^gon");
player->print(".\n");
if(pref->flag == P_NO_TRACK_STATS)
player->print("Note that player kill statistics are always tracked and cannot be reset.\n");
return(0);
}