/*
* color.cpp
* Functions to handle color.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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 "login.h"
#include "commands.h"
#include <sstream>
#define CLEAR "\033[0m" // Resets color
#define C_BLACK "\033[0;30m" // Normal colors
#define C_RED "\033[0;31m"
#define C_GREEN "\033[0;32m"
#define C_YELLOW "\033[0;33m"
#define C_BLUE "\033[0;34m"
#define C_MAGENTA "\033[0;35m"
#define C_CYAN "\033[0;36m"
#define C_WHITE "\033[0;37m"
#define C_D_GREY "\033[1;30m" // Light Colors
#define C_B_RED "\033[1;31m"
#define C_B_GREEN "\033[1;32m"
#define C_B_YELLOW "\033[1;33m"
#define C_B_BLUE "\033[1;34m"
#define C_B_MAGENTA "\033[1;35m"
#define C_B_CYAN "\033[1;36m"
#define C_B_WHITE "\033[1;37m"
#define C_BOLD "\033[1m"
#define C_BLINK "\033[5m"
int Ansi[12] = { 30, 31, 32, 33, 34, 35, 36, 37, 1, 0, 5 };
int Mirc[9] = { 1, 4, 9, 8, 12, 13, 11, 15, 15 };
//***********************************************************************
// nameToColorCode
//***********************************************************************
char nameToColorCode(bstring name) {
name = name.toLower();
if(name == "blink")
return('!');
if(name == "blue")
return('b');
if(name == "bold blue")
return('B');
if(name == "red")
return('r');
if(name == "bold red")
return('R');
if(name == "cyan")
return('c');
if(name == "bold cyan")
return('C');
if(name == "green")
return('g');
if(name == "bold green")
return('G');
if(name == "magenta")
return('m');
if(name == "bold magenta")
return('M');
if(name == "yellow")
return('y');
if(name == "bold yellow")
return('Y');
if(name == "white")
return('w');
if(name == "bold white")
return('W');
if(name == "black")
return('d');
if(name == "grey")
return('D');
if(name == "gold")
return('l');
if(name == "cerulean")
return('e');
if(name == "pink")
return('p');
if(name == "sky blue")
return('s');
if(name == "brown")
return('o');
return('x');
}
//***********************************************************************
// getColor
//***********************************************************************
const char* getAnsiColorCode(const unsigned char ch) {
switch(ch) {
case 'd': return(C_BLACK);
case 'b': return(C_BLUE);
case 'E':
case 'c': return(C_CYAN);
case 'g': return(C_GREEN);
case 'm': return(C_MAGENTA);
case 'r': return(C_RED);
case 'w': return(C_WHITE);
case 'o':
case 'y': return(C_YELLOW);
case 'B': return(C_B_BLUE);
case 'e':
case 'C': return(C_B_CYAN);
case 'G': return(C_B_GREEN);
case 'M': return(C_B_MAGENTA);
case 'R': return(C_B_RED);
case 'W': return(C_B_WHITE);
case 'Y': return(C_B_YELLOW);
case 'D': return(C_D_GREY);
case '@': return(C_BOLD);
case '#': return(C_BLINK);
case '^': return("^");
case 'x':
default: return(CLEAR C_WHITE);
}
}
//*********************************************************************
// getCustomColor
//**********************************************************************
bstring Player::getCustomColor(CustomColor i, bool caret) const {
char color;
if(customColors[(int)i] != CUSTOM_COLOR_DEFAULT) {
color = customColors[(int)i];
if(color == '!')
color = '#';
return((bstring)(caret?"^":"") + color);
}
return(gConfig->getCustomColor(i, caret));
}
bstring Config::getCustomColor(CustomColor i, bool caret) const {
if(customColors[(int)i] == CUSTOM_COLOR_DEFAULT)
return((bstring)(caret?"^":"") + "x");
return((bstring)(caret?"^":"") + customColors[(int)i]);
}
//*********************************************************************
// customColorize
//**********************************************************************
const bstring Monster::customColorize(bstring text, bool caret) const {
if(getMaster() && getMaster()->isPlayer())
text = getMaster()->getAsConstPlayer()->customColorize(text, caret);
return(text);
}
const bstring Player::customColorize(bstring text, bool caret) const {
text.Replace("*CC:BROADCAST*", getCustomColor(CUSTOM_COLOR_BROADCAST, caret).c_str());
text.Replace("*CC:GOSSIP*", getCustomColor(CUSTOM_COLOR_GOSSIP, caret).c_str());
text.Replace("*CC:PTEST*", getCustomColor(CUSTOM_COLOR_PTEST, caret).c_str());
text.Replace("*CC:NEWBIE*", getCustomColor(CUSTOM_COLOR_NEWBIE, caret).c_str());
text.Replace("*CC:DM*", getCustomColor(CUSTOM_COLOR_DM, caret).c_str());
text.Replace("*CC:ADMIN*", getCustomColor(CUSTOM_COLOR_ADMIN, caret).c_str());
text.Replace("*CC:SEND*", getCustomColor(CUSTOM_COLOR_SEND, caret).c_str());
text.Replace("*CC:MESSAGE*", getCustomColor(CUSTOM_COLOR_MESSAGE, caret).c_str());
text.Replace("*CC:WATCHER*", getCustomColor(CUSTOM_COLOR_WATCHER, caret).c_str());
text.Replace("*CC:CLASS*", getCustomColor(CUSTOM_COLOR_CLASS, caret).c_str());
text.Replace("*CC:RACE*", getCustomColor(CUSTOM_COLOR_RACE, caret).c_str());
text.Replace("*CC:CLAN*", getCustomColor(CUSTOM_COLOR_CLAN, caret).c_str());
text.Replace("*CC:TELL*", getCustomColor(CUSTOM_COLOR_TELL, caret).c_str());
text.Replace("*CC:GROUP*", getCustomColor(CUSTOM_COLOR_GROUP, caret).c_str());
text.Replace("*CC:DAMAGE*", getCustomColor(CUSTOM_COLOR_DAMAGE, caret).c_str());
text.Replace("*CC:SELF*", getCustomColor(CUSTOM_COLOR_SELF, caret).c_str());
text.Replace("*CC:GUILD*", getCustomColor(CUSTOM_COLOR_GUILD, caret).c_str());
return(text);
}
//*********************************************************************
// resetCustomColors
//**********************************************************************
void Player::resetCustomColors() {
memset(customColors, CUSTOM_COLOR_DEFAULT, sizeof(customColors));
customColors[CUSTOM_COLOR_SIZE-1] = 0;
}
//***********************************************************************
// cmdColors
//***********************************************************************
// this function demonstrates all the color options available
// Letters Remaining:
// :: a f hijk no q tuv z
// :: A F HIJKL NOPQ STUV X Z
const char* colorSection(bool staff, const char* color, char colorChar = 0) {
static char str[80];
char code[10];
code[0] = 0;
if(colorChar == 0)
colorChar = nameToColorCode(color);
// staff get to see the color code
if(staff)
sprintf(code, "^x^^%c: ", colorChar);
sprintf(str, "%s^%c%-10s", code, colorChar, color);
return(str);
}
int cmdColors(Player* player, cmd* cmnd) {
bool staff = player->isStaff();
// player->defineMXP();
if(!strcmp(cmnd->str[1], "reset")) {
player->print("Custom colors have been reset to defaults.\n");
player->resetCustomColors();
return(0);
} else if(cmnd->num > 2) {
CustomColor i;
bstring type = cmnd->str[1];
type = type.toLower();
if(type == "broadcast")
i = CUSTOM_COLOR_BROADCAST;
else if((type == "ptest" || type == "p-test") && isPtester(player))
i = CUSTOM_COLOR_PTEST;
else if(type == "gossip")
i = CUSTOM_COLOR_GOSSIP;
else if(type == "newbie")
i = CUSTOM_COLOR_NEWBIE;
else if(type == "dm" && player->isDm())
i = CUSTOM_COLOR_DM;
else if(type == "admin" && player->isAdm())
i = CUSTOM_COLOR_ADMIN;
else if(type == "send" && player->isCt())
i = CUSTOM_COLOR_SEND;
else if(type == "message" && player->isStaff())
i = CUSTOM_COLOR_MESSAGE;
else if(type == "watcher" && player->isWatcher())
i = CUSTOM_COLOR_WATCHER;
else if(type == "class")
i = CUSTOM_COLOR_CLASS;
else if(type == "race")
i = CUSTOM_COLOR_RACE;
else if(type == "clan")
i = CUSTOM_COLOR_CLAN;
else if(type == "tell")
i = CUSTOM_COLOR_TELL;
else if(type == "group")
i = CUSTOM_COLOR_GROUP;
else if(type == "damage")
i = CUSTOM_COLOR_DAMAGE;
else if(type == "self")
i = CUSTOM_COLOR_SELF;
else if(type == "guild")
i = CUSTOM_COLOR_GUILD;
else {
player->print("Custom color type choice not understood.\n");
return(0);
}
char color = nameToColorCode(getFullstrText(cmnd->fullstr, 2));
player->setCustomColor(i, color);
if(color == '!')
color = '#';
player->printColor("The ^%ccustom color^x has been set.\n", color);
return(0);
}
// must be on separate lines because colorSection uses a static char
player->print("Colors:\n");
player->printColor(" %s", colorSection(staff, "Blue"));
player->printColor(" %s\n", colorSection(staff, "Bold Blue"));
player->printColor(" %s", colorSection(staff, "Red"));
player->printColor(" %s\n", colorSection(staff, "Bold Red"));
player->printColor(" %s", colorSection(staff, "Cyan"));
player->printColor(" %s\n", colorSection(staff, "Bold Cyan"));
player->printColor(" %s", colorSection(staff, "Green"));
player->printColor(" %s\n", colorSection(staff, "Bold Green"));
player->printColor(" %s", colorSection(staff, "Magenta"));
player->printColor(" %s\n", colorSection(staff, "Bold Magenta"));
player->printColor(" %s", colorSection(staff, "Yellow"));
player->printColor(" %s\n", colorSection(staff, "Bold Yellow"));
player->printColor(" %s", colorSection(staff, "White"));
player->printColor(" %s\n", colorSection(staff, "Bold White"));
player->printColor(" %s", colorSection(staff, "Black"));
player->printColor(" %s\n", colorSection(staff, "Grey"));
player->printColor(" %s\n", colorSection(staff, "Blink", '#'));
player->print("\n");
player->print("New MXP Colors:\n");
if(!player->flagIsSet(P_MXP_ENABLED))
player->printColor(" ^yYou will need to type \"set mxpcolors\" to enable the following colors.\n");
player->printColor(" %s", colorSection(staff, "Gold"));
player->printColor(" %s\n", colorSection(staff, "Cerulean"));
player->printColor(" %s", colorSection(staff, "Pink"));
player->printColor(" %s\n", colorSection(staff, "Sky Blue"));
player->printColor(" %s\n", colorSection(staff, "Brown"));
player->print("\n");
player->printColor("Custom Colors: type [color ^W<type> <color>^x]\n");
player->printColor(" Choose ^Wtype^x from below, ^Wcolor^x from above.\n");
player->printColor(" Type ^Wcolor reset^x to return to default colors.\n\n");
std::map<bstring,bstring> options;
std::map<bstring,bstring>::iterator it;
options["Self: @"] = player->customColorize("*CC:SELF*");
options["Broadcast"] = player->customColorize("*CC:BROADCAST*");
options["Tell"] = player->customColorize("*CC:TELL*");
options["Gossip"] = player->customColorize("*CC:GOSSIP*");
options["Newbie"] = player->customColorize("*CC:NEWBIE*");
options["Class"] = player->customColorize("*CC:CLASS*");
options["Race"] = player->customColorize("*CC:RACE*");
options["Clan"] = player->customColorize("*CC:CLAN*");
options["Group"] = player->customColorize("*CC:GROUP*");
options["Damage"] = player->customColorize("*CC:DAMAGE*");
if(player->isDm())
options["DM"] = player->customColorize("*CC:DM*");
if(player->isAdm())
options["Admin"] = player->customColorize("*CC:ADMIN*");
if(player->isCt())
options["Send"] = player->customColorize("*CC:SEND*");
if(player->isStaff())
options["Message"] = player->customColorize("*CC:MESSAGE*");
if(player->isWatcher())
options["Watcher"] = player->customColorize("*CC:WATCHER*");
if(isPtester(player))
options["P-Test"] = player->customColorize("*CC:PTEST*");
if(player->getGuild())
options["Guild"] = player->customColorize("*CC:GUILD*");
for(it = options.begin() ; it != options.end() ; ) {
for(int n=0; n<3; n++) {
if(it != options.end()) {
player->printColor(" ^x* %s%-10s", (*it).second.c_str(), (*it).first.c_str());
it++;
}
}
player->print("\n");
}
player->print("\n");
return(0);
}
//***********************************************************************
// defineColors
//***********************************************************************
// Set color flags according to negotiated telnet options on character
// creation. Character flags override these settings after creation
void Player::defineColors() {
if(mySock->getColorOpt() == ANSI_COLOR) {
setFlag(P_ANSI_COLOR);
clearFlag(P_MXP_ENABLED);
clearFlag(P_MIRC);
clearFlag(P_NEWLINE_AFTER_PROMPT);
}
else if(mySock->getColorOpt() == MXP_COLOR) {
setFlag(P_ANSI_COLOR);
setFlag(P_MXP_ENABLED);
clearFlag(P_MIRC);
clearFlag(P_NEWLINE_AFTER_PROMPT);
mySock->defineMxp();
} else if(mySock->getColorOpt() == NO_COLOR) {
clearFlag(P_ANSI_COLOR);
clearFlag(P_MXP_ENABLED);
clearFlag(P_MIRC);
clearFlag(P_NEWLINE_AFTER_PROMPT);
}
}
//***********************************************************************
// setSocketColors
//***********************************************************************
// Set color options on the socket according to player flags (which can
// overide what we've negotiated)
void Player::setSockColors() {
if(flagIsSet(P_ANSI_COLOR)) {
if(flagIsSet(P_MXP_ENABLED) && mySock->getMxp()) {
mySock->setColorOpt(MXP_COLOR);
} else {
mySock->setColorOpt(ANSI_COLOR);
}
} else {
mySock->setColorOpt(NO_COLOR);
}
}
//***********************************************************************
// ANSI
//***********************************************************************
void ANSI(Socket* sock, int color) {
if(sock && sock->getPlayer()) {
if(sock->getPlayer()->flagIsSet(P_ANSI_COLOR)) {
if(color & BOLD) sock->print("%c[%dm", 27, Ansi[COLOR_BOLD]);
if(color & BLINK) sock->print("%c[%dm", 27, Ansi[COLOR_BLINK]);
if(color & NORMAL) sock->print("%c[%dm", 27, Ansi[COLOR_NORMAL]);
if(color & RED) sock->print("%c[%dm", 27, Ansi[COLOR_RED]);
else if(color & GREEN) sock->print("%c[%dm", 27, Ansi[COLOR_GREEN]);
else if(color & YELLOW) sock->print("%c[%dm", 27, Ansi[COLOR_YELLOW]);
else if(color & BLUE) sock->print("%c[%dm", 27, Ansi[COLOR_BLUE]);
else if(color & MAGENTA) sock->print("%c[%dm", 27, Ansi[COLOR_MAGENTA]);
else if(color & CYAN) sock->print("%c[%dm", 27, Ansi[COLOR_CYAN]);
else if(color & BLACK) sock->print("%c[%dm", 27, Ansi[COLOR_BLACK]);
else if(color & WHITE) sock->print("%c[%dm", 27, Ansi[COLOR_WHITE]);
} else if(sock->getPlayer()->flagIsSet(P_MIRC) && color != BLINK) {
if(color & BOLD) sock->print("%c", 2);
if(color & UNDERLINE) sock->print("%c", 31);
if(color & NORMAL) sock->print("%c%c", 3, 3);
if(color & RED) sock->print("%c%d", 3, Mirc[COLOR_RED]);
else if(color & GREEN) sock->print("%c%d", 3, Mirc[COLOR_GREEN]);
else if(color & YELLOW) sock->print("%c%d", 3, Mirc[COLOR_YELLOW]);
else if(color & BLUE) sock->print("%c%d", 3, Mirc[COLOR_BLUE]);
else if(color & MAGENTA) sock->print("%c%d", 3, Mirc[COLOR_MAGENTA]);
else if(color & CYAN) sock->print("%c%d", 3, Mirc[COLOR_CYAN]);
else if(color & BLACK) sock->print("%c%d", 3, Mirc[COLOR_BLACK]);
else if(color & WHITE) sock->print("%c%d", 3, Mirc[COLOR_WHITE]);
}
}
}
//***********************************************************************
// getMXPColor
//***********************************************************************
// This function is being called during parseForOutput so we must use the actual mxp tags
// We're also assuming we get a valid mxp color tag here
bstring getMxpColorTag(bstring str, bool open) {
std::ostringstream oStr;
oStr << MXP_SECURE_OPEN << "<";
if(!open)
oStr << "/";
oStr << gConfig->getMxpColorTag(str);
oStr << ">" << MXP_LOCK_CLOSE;
return(oStr.str());
}
// TODO: Put this in a lookup table
bool isMxpColor(const unsigned char ch) {
switch(ch) {
case 'l':
case 'e':
case 'p':
case 's':
case 'E':
case 'o':
return(true);
}
return(false);
}
//***********************************************************************
// getMXPColor
//***********************************************************************
// Get the color code we need to print
// Currently handles MXP & ANSI Color
// TODO: Handle xterm256 color
bstring Socket::getColorCode(const unsigned char ch) {
std::ostringstream oStr;
if(!opts.color) {
// Color is not active, only replace a caret
if(ch == '^')
oStr << "^";
return(oStr.str());
} else {
// Color is active, do replacement
// Only return a color if the last color is not equal to the current color
if(opts.lastColor != ch || opts.lastColor == '^') {
if(opts.color == MXP_COLOR) {
// Check if we need to close a mxp color
if(isMxpColor(opts.lastColor)) {
oStr << getMxpColorTag(bstring(1,opts.lastColor), false);
}
// Now check if the new color is mxp, if it is we're done here
// and can return now, otherwise continue on and check for an ANSI
// color
if(isMxpColor(ch)) {
opts.lastColor = ch;
oStr << getMxpColorTag(bstring(1,ch), true);
return(oStr.str());
}
}
// Now Handle ANSI
opts.lastColor = ch;
oStr << getAnsiColorCode(ch);
}
return(oStr.str());
}
}
//***********************************************************************
// stripColor
//***********************************************************************
bstring stripColor(bstring color) {
std::ostringstream str;
unsigned int i=0, max = color.getLength();
for(; i < max ; i++) {
if(color.getAt(i) == '^')
i++;
else
str << color.getAt(i);
}
return(str.str());
}
//***********************************************************************
// escapeColor
//***********************************************************************
bstring escapeColor(bstring color) {
color.Replace("^","^^");
return(color);
}