/* * MusicMUD - Colour and Character Set Module * Copyright (C) 1998-2003 Abigail Brady * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <string.h> #include <ctype.h> #include <stdio.h> #include "msi.h" #include "Socket.h" #include "musicmud.h" #include "privs.h" #include "format.h" #include "util.h" #include "pflags.h" #include "verbs.h" #include "misc.h" #define MODULE "colour" #include "wcwidth.c" //! the blank colours. const colstate_t blank; const char *namecol(int col) { static const char *cols[] = {"black", "red", "green", "yellow", "blue", "mangenta", "cyan", "white"}; return cols[col & 7]; } static string desccol(int fgcol) { if (fgcol == 0) return "#000000"; if (fgcol == 1) return "#d00000"; if (fgcol == 2) return "#00d000"; if (fgcol == 3) return "#d0d000"; if (fgcol == 4) return "#0000d0"; if (fgcol == 5) return "#d000d0"; if (fgcol == 6) return "#00d0d0"; if (fgcol == 7) return "#d0d0d0"; if (fgcol == 8) return "#808080"; if (fgcol == 9) return "#ff0000"; if (fgcol == 10) return "#00ff00"; if (fgcol == 11) return "#ffff00"; if (fgcol == 12) return "#0000ff"; if (fgcol == 13) return "#ff00ff"; if (fgcol == 14) return "#00ffff"; if (fgcol == 15) return "#ffffff"; if (fgcol >= 16 && fgcol <= 232) { const char *scale[] = {"00", "5f", "87", "af", "df", "ff"}; fgcol -= 16; int red = (fgcol / 36) % 6; int grn = (fgcol / 6) % 6; int blu = (fgcol) % 6; return ssprintf("#%s%s%s", scale[red], scale[grn], scale[blu]); } return "????"; } static string make_mxpdifference(colstate_t ¤t, const colstate_t &need) { string s; colstate_t needed = need; if (current.bgcol == -1) current.bgcol = 0; if (current.fgcol == -1) current.fgcol = 7; if (current.bold) current.fgcol |= 8; if (needed.bgcol == -1) needed.bgcol = 0; if (needed.fgcol == -1) needed.fgcol = 7; if (needed.bold) needed.fgcol |= 8; if (current.sout && (!needed.sout || current.sout != needed.sout || current.fgcol != needed.fgcol || current.bgcol != needed.bgcol || current.undl != needed.undl)) { s += "</s>"; current.sout = 0; } if (current.ital && (!needed.ital || current.fgcol != needed.fgcol || current.bgcol != needed.bgcol || current.undl != needed.undl)) { s += "</i>"; current.ital = 0; } if (current.undl && (!needed.undl || current.fgcol != needed.fgcol || current.bgcol != needed.bgcol)) { s += "</u>"; current.undl = 0; } if (current.fgcol != needed.fgcol || needed.bgcol != needed.bgcol) { if ((current.fgcol != 7 || current.bgcol != 0)) s += ssprintf("</color>"); if (needed.fgcol != 7 || needed.bgcol != 0) s += ssprintf("<color fore=%s back=%s>", desccol(needed.fgcol), desccol(needed.bgcol)); current.fgcol = needed.fgcol; current.bgcol = needed.bgcol; } if (needed.undl && !current.undl) { s += "<u>"; current.undl = 1; } if (needed.ital && !current.ital) { s += "<i>"; current.ital = 1; } if (needed.sout && !current.sout) { s += "<s>"; current.sout = 1; } return s; } //! spit out the escape sequence to get from current to needed, and update current with needed static string make_difference(colstate_t ¤t, const colstate_t &needed) { string esc = ""; if (current.bold && !needed.bold || current.undl && !needed.undl || current.ital && !needed.ital || current.sout && !needed.sout || (needed.fgcol == -1 && current.fgcol != -1) || (needed.bgcol == -1 && current.bgcol != -1)) { esc += "0;"; int olddec = current.dec; current = blank; current.dec = olddec; } if (current.bold != needed.bold) { esc += needed.bold?"1;":"22;"; } if (current.undl != needed.undl) { esc += needed.undl?"4;":"24;"; } if (current.ital != needed.ital) { esc += needed.ital?"3;":"23;"; } if (current.sout != needed.sout) { esc += needed.sout?"9;":"29;"; } if (current.fgcol != needed.fgcol) { char blah[20]; if (needed.fgcol < 8) sprintf(blah, "%i;", 30+needed.fgcol); else sprintf(blah, "38;5;%i;", needed.fgcol); esc += blah; } if (current.bgcol != needed.bgcol) { char blah[20]; if (needed.bgcol < 8) sprintf(blah, "%i;", 40+needed.bgcol); else sprintf(blah, "48;5;%i;", needed.bgcol); esc += blah; } esc = esc.substr(0, esc.length()-1); string buf = ""; if (esc.length()) { buf = "\033["; buf += esc; buf += "m"; } if (current.dec != needed.dec) { if (needed.dec) buf += "\033(0"; else buf += "\033(B"; } current = needed; return buf; } //! spit out the difference to get from current to needed, but only the DEC part of it static string make_decdifference(colstate_t ¤t, const colstate_t &needed) { string buf; if (current.dec != needed.dec) { if (needed.dec) buf += "\033(0"; else buf += "\033(B"; } current = needed; return buf; } #define HAS_256COLS 1 #define NO_ANSI 2 #define CRLF 4 #define DEC 16 #define JUSTLENGTH 64 //! charcode->colourindex map static char *bcols = "lrgybmcw"; //! lookup a 'classic' colour static char lookup_classic(char w) { if (w=='S') return 'G'; if (w=='o') return 'c'; if (w=='p') return 'C'; if (w=='P') return 'B'; if (w=='t') return 'y'; if (w=='s') return 'g'; if (w=='k') return 'y'; if (w=='i') return 'Y'; if (w=='d') return 'W'; if (w=='D') return 'C'; return w; } //! lookup a 'paper' colour static char lookup_paper(char w) { if (w=='o') return 'b'; if (w=='p') return 'R'; if (w=='d') return 'L'; if (w=='D') return 'l'; w = lookup_classic(w); if (w=='w') return 'l'; if (w=='W') return 'L'; if (w=='Y' || w=='y') return 'r'; if (w=='l') return 'W'; if (w=='L') return 'w'; return w; } //! lookup a 'yellow' colour static char lookup_yellow(char w) { if (w=='d') return 'Y'; if (w=='D') return 'y'; return lookup_classic(w); } //! lookup a 'red' colour static char lookup_red(char w) { if (w=='o') return 'r'; if (w=='P') return 'R'; if (w=='p') return 'Y'; return lookup_classic(w); } //! a colour-lookuper typedef char(*lookup_t)(char); //! lookup a colour in the given scheme static char lookup_col(char what, const char *scheme) { if (!scheme) return what; lookup_t a = lookup_classic; if (!strcmp(scheme, "yellow")) a = lookup_yellow; if (!strcmp(scheme, "red")) a = lookup_red; if (!strcmp(scheme, "paper")) a = lookup_paper; return a(what); } //! try to set the foreground colour static bool setfgcol(colstate_t &needed, char code, const Player *who, int flags, const char *scheme, int tripping=0) { if (who) { char name[256]; sprintf(name, "col.%c", code); const char *of = who->get(name); if (of && strlen(of)==2 && of[0]=='^') { code = of[1]; } else if (of && strlen(of)==5) { if (flags & HAS_256COLS) { const char *src = of+1; int rd = src[0]-'0'; int g = src[1]-'0'; int b = src[2]-'0'; if (rd>5) rd = 5; if (g>5) g = 5; if (b>5) b = 5; needed.fgcol = 16 + (rd * 36 + g * 6 + b); needed.bold = isupper(*src); return 1; } else { code = of[4]; } } code = lookup_col(code, scheme); } else code = lookup_col(code, scheme); char * wh = strchr(bcols, tolower(code)); if (wh) { if (!tripping) { needed.fgcol = wh - bcols; needed.bold = isupper(code); } return 1; } return 0; } /* This needs rewriting to be two different tables 1 - mud code -> UCS lookup 2 - UCS fallbacks for various values */ //! struct ucs_entry { char code; const char *unistr; const char *latin1; const char *ascii; char vt100; int uni; } whats[] = { { '^', "^","^", "^", 0, 0 }, /* CIRCUMFLEX ACCENT */ { '+', 0, "+", "+", 0x6e, 0x253c }, /* FORMS HORIZONTAL AND VERTICAL */ { '|', 0, "|", "|", 0x78, 0x2502 }, /* FORMS VERTICAL */ { '-', 0, "-", "-", 0x71, 0x2500 }, /* FORMS HORIZONTAL */ { '/', 0, "|", "|", 0x6c, 0x250c }, /* FORMS DOWN AND RIGHT (256d) */ { '\\', 0, "|", "|", 0x6b, 0x2510 }, /* FORMS DOWN AND LEFT 256e */ { '[', 0, "|", "|", 0x6d, 0x2514 }, /* FORMS UP AND RIGHT 2570 */ { ']', 0, "|", "|", 0x6a, 0x2518 }, /* FORMS UP AND LEFT 256f */ { '>', 0, "|", "|", 0x74, 0x251c }, /* FORMS VERTICAL AND RIGHT */ { '<', 0, "|", "|", 0x75, 0x2524 }, /* FORMS VERTICAL AND LEFT */ { '!', 0, "-", "-", 0x77, 0x252c }, /* FORMS DOWN AND HORIZONTAL */ { '~', 0, "-", "-", 0x76, 0x2534 }, /* FORMS UP AND HORIZONTAL */ { 'O', 0, "M", "M", 0, 0x2642 }, /* MALE SIGN */ { 'F', 0, "F", "F", 0, 0x2640 }, /* FEMALE SIGN */ { '=', 0, "=", "=", 0, 0x2550 }, /* FORMS DOUBLE HORIZONTAL */ { 0, 0, 0 , 0, 96 , 0x25c6}, /* DIAMOND */ { 0, 0, 0 , 0, 'a', 0x2592}, /* CHECKERBOARD */ { 0, 0, 0 , "HT", 'b', 0x2409}, /* HT */ { 0, 0, 0 , "FF", 'c', 0x240c}, /* FF */ { 0, 0, 0 , "CR", 'd', 0x240d}, /* CR */ { 0, 0, 0 , "LF", 'e', 0x240a}, /* LF */ { 0, 0, 0 , "NL", 'h', 0x2424}, /* NL */ { 0, 0, 0 , "VT", 'i', 0x240b}, /* VT */ { 0, 0, 0 , "<=", 'y', 0x2264}, /* <= */ { 0, 0, 0 , ">=", 'z', 0x2265}, /* >= */ // { 0, 0, 0 , 0, '{', 0x03c0}, /* PI */ { 0, 0, 0 , "!=", '|', 0x2260}, /* != */ { 0, 0, 0 , "O", 0, 0x25cf}, /* FILLED CIRCLE */ { 0, 0, 0, "S", 0, 0x2660}, /* SPADE */ { 0, 0, 0, "H", 0, 0x2665}, /* HEART */ { 0, 0, 0, "C", 0, 0x2663}, /* CLUB */ { 0, 0, 0, "D", 0, 0x2666}, /* DIAMOND */ { 0, 0, 0, "o/~", 0, 9834}, /* NOTE */ { 0, 0, 0, "E", 0, 0x20ac}, {0, 0, 0, " ", 0, 0x00a0}, {0, 0, 0, "!", 0, 0x00a1}, {0, 0, 0, "c", 0, 0x00a2}, {0, 0, 0, "L", '}', 0x00a3}, {0, 0, 0, "#", 0, 0x00a4}, {0, 0, 0, "Y", 0, 0x00a5}, {0, 0, 0, "|", 0, 0x00a6}, {0, 0, 0, "S", 0, 0x00a7}, {0, 0, 0, "\"", 0, 0x00a8}, {0, 0, 0, "(c)", 0, 0x00a9}, {0, 0, 0, "a", 0, 0x00aa}, {0, 0, 0, "<<", 0, 0x00ab}, {0, 0, 0, "!", 0, 0x00ac}, {0, 0, 0, "-", 0, 0x00ad}, {0, 0, 0, "(r)", 0, 0x00ae}, {0, 0, 0, "~", 0, 0x00af}, {0, 0, 0, "o", 'f', 0x00b0}, {0, 0, 0, "+-", 'g', 0x00b1}, {0, 0, 0, "", 0, 0x00b2}, {0, 0, 0, "~", 0, 0x00b3}, {0, 0, 0, "'", 0, 0x00b4}, {0, 0, 0, "u", 0, 0x00b5}, {0, 0, 0, "P", 0, 0x00b6}, {0, 0, 0, ".", 0, 0x00b7}, {0, 0, 0, ",", 0, 0x00b8}, {0, 0, 0, "-", 0, 0x00b9}, {0, 0, 0, "o", 0, 0x00ba}, {0, 0, 0, ">>", 0, 0x00bb}, {0, 0, 0, "1/4", 0, 0x00bc}, {0, 0, 0, "1/2", 0, 0x00bd}, {0, 0, 0, "3/4", 0, 0x00be}, {0, 0, 0, "?", 0, 0x00bf}, {0, 0, 0, "A", 0, 0x00c0}, {0, 0, 0, "A", 0, 0x00c1}, {0, 0, 0, "A", 0, 0x00c2}, {0, 0, 0, "A", 0, 0x00c3}, {0, 0, 0, "A", 0, 0x00c4}, {0, 0, 0, "A", 0, 0x00c5}, {0, 0, 0, "AE", 0, 0x00c6}, {0, 0, 0, "C", 0, 0x00c7}, {0, 0, 0, "E", 0, 0x00c8}, {0, 0, 0, "E", 0, 0x00c9}, {0, 0, 0, "E", 0, 0x00ca}, {0, 0, 0, "E", 0, 0x00cb}, {0, 0, 0, "I", 0, 0x00cc}, {0, 0, 0, "I", 0, 0x00cd}, {0, 0, 0, "I", 0, 0x00ce}, {0, 0, 0, "I", 0, 0x00cf}, {0, 0, 0, "Dh", 0, 0x00d0}, {0, 0, 0, "N", 0, 0x00d1}, {0, 0, 0, "O", 0, 0x00d2}, {0, 0, 0, "O", 0, 0x00d3}, {0, 0, 0, "O", 0, 0x00d4}, {0, 0, 0, "O", 0, 0x00d5}, {0, 0, 0, "O", 0, 0x00d6}, {0, 0, 0, "x", 0, 0x00d7}, {0, 0, 0, "O", 0, 0x00d8}, {0, 0, 0, "U", 0, 0x00d9}, {0, 0, 0, "U", 0, 0x00da}, {0, 0, 0, "U", 0, 0x00db}, {0, 0, 0, "U", 0, 0x00dc}, {0, 0, 0, "Y", 0, 0x00dd}, {0, 0, 0, "Th", 0, 0x00de}, {0, 0, 0, "ss", 0, 0x00df}, {0, 0, 0, "a", 0, 0x00e0}, {0, 0, 0, "a", 0, 0x00e1}, {0, 0, 0, "a", 0, 0x00e2}, {0, 0, 0, "a", 0, 0x00e3}, {0, 0, 0, "a", 0, 0x00e4}, {0, 0, 0, "a", 0, 0x00e5}, {0, 0, 0, "ae", 0, 0x00e6}, {0, 0, 0, "c", 0, 0x00e7}, {0, 0, 0, "e", 0, 0x00e8}, {0, 0, 0, "e", 0, 0x00e9}, {0, 0, 0, "e", 0, 0x00ea}, {0, 0, 0, "e", 0, 0x00eb}, {0, 0, 0, "i", 0, 0x00ec}, {0, 0, 0, "i", 0, 0x00ed}, {0, 0, 0, "i", 0, 0x00ee}, {0, 0, 0, "i", 0, 0x00ef}, {0, 0, 0, "dh", 0, 0x00f0}, {0, 0, 0, "n", 0, 0x00f1}, {0, 0, 0, "o", 0, 0x00f2}, {0, 0, 0, "o", 0, 0x00f3}, {0, 0, 0, "o", 0, 0x00f4}, {0, 0, 0, "o", 0, 0x00f5}, {0, 0, 0, "o", 0, 0x00f6}, {0, 0, 0, "/", 0, 0x00f7}, {0, 0, 0, "o", 0, 0x00f8}, {0, 0, 0, "u", 0, 0x00f9}, {0, 0, 0, "u", 0, 0x00fa}, {0, 0, 0, "u", 0, 0x00fb}, {0, 0, 0, "u", 0, 0x00fc}, {0, 0, 0, "y", 0, 0x00fd}, {0, 0, 0, "th", 0, 0x00fe}, {0, 0, 0, "y", 0, 0x00ff}, #define CYR(a, b) { 0, 0, 0, a, 0, b } CYR("A", 0x104), CYR("a", 0x105), CYR("C", 0x106), CYR("c", 0x107), CYR("E", 0x118), CYR("e", 0x119), CYR("L", 0x141), CYR("l", 0x142), CYR("E", 0x403), CYR("I", 0x406), CYR("A", 0x410), CYR("B", 0x411), CYR("V", 0x412), CYR("G", 0x413), CYR("D", 0x414), CYR("E", 0x415), CYR("Zh", 0x416), CYR("Z", 0x417), CYR("I", 0x418), CYR("J", 0x419), CYR("K", 0x41a), CYR("L", 0x41b), CYR("M", 0x41c), CYR("N", 0x41d), CYR("O", 0x41e), CYR("P", 0x41f), CYR("R", 0x420), CYR("S", 0x421), CYR("T", 0x422), CYR("U", 0x423), CYR("F", 0x424), CYR("H", 0x425), CYR("C", 0x426), CYR("Ch", 0x427), CYR("Sh", 0x428), CYR("Shch", 0x429), CYR("''", 0x42a), CYR("Y", 0x42b), CYR("'", 0x42c), CYR("E", 0x42d), CYR("U", 0x42e), CYR("A", 0x42f), CYR("E", 0x453), CYR("I", 0x456), CYR("a", 0x430), CYR("b", 0x431), CYR("v", 0x432), CYR("g", 0x433), CYR("d", 0x434), CYR("e", 0x435), CYR("zh", 0x436), CYR("z", 0x437), CYR("i", 0x438), CYR("j", 0x439), CYR("k", 0x43a), CYR("l", 0x43b), CYR("m", 0x43c), CYR("n", 0x43d), CYR("o", 0x43e), CYR("p", 0x43f), CYR("r", 0x440), CYR("s", 0x441), CYR("t", 0x442), CYR("u", 0x443), CYR("f", 0x444), CYR("h", 0x445), CYR("c", 0x446), CYR("ch", 0x447), CYR("sh", 0x448), CYR("shch", 0x449), CYR("''", 0x44a), CYR("y", 0x44b), CYR("'", 0x44c), CYR("e", 0x44d), CYR("u", 0x44e), CYR("a", 0x44f), CYR("a", 0x3b1), CYR("b", 0x3b2), CYR("g", 0x3b3), CYR("d", 0x3b4), CYR("e", 0x3b5), CYR("z", 0x3b6), CYR("h", 0x3b7), CYR("th", 0x3b8), CYR("i", 0x3b9), CYR("k", 0x3ba), CYR("l", 0x3bb), CYR("m", 0x3bc), CYR("n", 0x3bd), CYR("x", 0x3be), CYR("o", 0x3bf), CYR("p", 0x3c0), CYR("r", 0x3c1), CYR("s", 0x3c2), CYR("s", 0x3c3), CYR("t", 0x3c4), CYR("u", 0x3c5), CYR("ph", 0x3c6), CYR("ch", 0x3c7), CYR("ps", 0x3c8), CYR("o", 0x3c9), CYR("A", 0x391), CYR("B", 0x392), CYR("G", 0x393), CYR("D", 0x394), CYR("E", 0x395), CYR("Z", 0x396), CYR("H", 0x397), CYR("Th", 0x398), CYR("I", 0x399), CYR("K", 0x39a), CYR("L", 0x39b), CYR("M", 0x39c), CYR("N", 0x39d), CYR("X", 0x39e), CYR("O", 0x39f), CYR("P", 0x3a0), CYR("R", 0x3a1), CYR("S", 0x3a2), CYR("S", 0x3a3), CYR("T", 0x3a4), CYR("U", 0x3a5), CYR("Ph", 0x3a6), CYR("Ch", 0x3a7), CYR("Ps", 0x3a8), CYR("O", 0x3a9), CYR( "f", 0x16a0), CYR( "u", 0x16a2), CYR( "th", 0x16a6), CYR( "o", 0x16a9), CYR( "r", 0x16b1), CYR( "k", 0x16b3), CYR( "g", 0x16b7), CYR( "w", 0x16b9), CYR( "h", 0x16bb), CYR( "n", 0x16be), CYR( "i", 0x16c1), CYR( "g", 0x16c4), CYR( "eo", 0x16c7), CYR( "p", 0x16c8), CYR( "eo", 0x16c9), CYR( "s", 0x16cb), CYR( "t", 0x16cf), CYR( "b", 0x16d2), CYR( "e", 0x16d6), CYR( "m", 0x16d7), CYR( "l", 0x16da), CYR( "i", 0x16dd), CYR( "e", 0x16df), CYR( "d", 0x16de), CYR( "a", 0x16aa), CYR( "ae", 0x16ab), CYR( "y", 0x16a3), CYR( "y", 0x16e6), CYR( "ea", 0x16e0), CYR( " ", 0x16eb), CYR( ".", 0x16ec), CYR( "c", 0x16e3), CYR ( " ", 0x2800 ), CYR ( "a", 0x2801 ), CYR ( "b", 0x2803 ), CYR ( "c", 0x2809 ), CYR ( "d", 0x2819 ), CYR ( "e", 0x2811 ), CYR ( "f", 0x280b ), CYR ( "g", 0x281b ), CYR ( "h", 0x2813 ), CYR ( "i", 0x280a ), CYR ( "j", 0x281a ), CYR ( "k", 0x2805 ), CYR ( "l", 0x2807), CYR ( "m", 0x280d ), CYR ( "n", 0x281d ), CYR ( "o", 0x2815 ), CYR ( "p", 0x280f ), CYR ( "q", 0x281f ), CYR ( "r", 0x2817 ), CYR ( "s", 0x280e ), CYR ( "t", 0x281e ), CYR ( "u", 0x2825 ), CYR ( "v", 0x2827 ), CYR ( "w", 0x283a ), CYR ( "x", 0x282d ), CYR ( "y", 0x283d ), CYR ( "z", 0x2835 ), CYR ( "ch", 0x2821 ), CYR ( "gh", 0x2823 ), CYR ( "sh", 0x2829 ), CYR ( "th", 0x2839 ), CYR ( "wh", 0x2831 ), CYR ( "ed", 0x282b ), CYR ( "er", 0x283b ), CYR ( "ou", 0x2833 ), CYR ( "ow", 0x282a ), CYR ( "en", 0x2828 ), { '`', 0, "'", "'", 0, 0x2018 }, /* LEFT SINGLE QUATOATION MARK */ { '\'', 0, "'", "'", 0, 0x2019 }, /* RIGHT SINGLE QUOTATION MARK */ { '%', 0, "\"", "\"", 0, 0x201d }, /* RIGHT DOUBLE QUOTATION MARK */ { '\"', 0, "\"", "\"", 0, 0x201c }, /* LEFT DOUBLE QUOTATION MARK */ {0, 0, 0, "<", 0, 0x2190}, {0, 0, 0, "^", 0, 0x2191}, {0, 0, 0, ">", 0, 0x2192}, {0, 0, 0, "v", 0, 0x2193}, {0, 0, 0, " ", 0, 0x25ef}, }; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) //! comparator function static int cmp(struct ucs_entry *a, struct ucs_entry *b) { if (a->uni > b->uni) return 1; if (a->uni == b->uni) return 0; return -1; } //! print out all the graphics characters known static bool verb_graph(MudObject *who, int argc, const char **argv) { Divert d(who, "graph"); qsort(&whats, ARRAY_SIZE(whats), sizeof(struct ucs_entry), (int(*)(const void *, const void *))cmp); int j = 0; for (size_t i=0;i<ARRAY_SIZE(whats);i++) { if (whats[i].uni && whats[i].code) { string s = "^"; s += whats[i].code; who->printf("%04X %3s ^^%c ", whats[i].uni, s.c_str(), whats[i].code); j++; } else if (whats[i].uni) { string s = ssprintf("^#%i;", whats[i].uni); who->printf("%04X %3s ^^#x%04x; ", whats[i].uni, s.c_str(), whats[i].uni); j++; } else continue; if ((j & 3)==0) { who->printf("\n"); } } who->printf("\n"); return true; } //! convert UCS to UTF-8 static string unitoutf8(int i) { if (i < 0x80) { char b2[2] = {i, 0 }; return b2; } if (i >= 0x80 && i <= 0x800) { char b2[3]; b2[0] = 0xc0 | (i >> 6); b2[1] = 0x80 | (i & 0x3f); b2[2] = 0; return b2; } if (i < 0x10000) { char b2[4]; b2[0] = 0xe0 | (i >> 12); b2[1] = 0x80 | ((i >> 6) & 0x3f); b2[2] = 0x80 | ((i >> 0) & 0x3f); b2[3] = 0; return b2; } if (i < 0x00200000) { char b2[5]; b2[0] = 0xe0 | (i >> 18); b2[1] = 0x80 | ((i >> 12) & 0x3f); b2[2] = 0x80 | ((i >> 6) & 0x3f); b2[3] = 0x80 | ((i >> 0) & 0x3f); b2[4] = 0; return b2; } return "?"; } //! obtain the best version of /ch/ we can given the given settings static string downgrade(int ch, int flags, charset cset, int *dec=0) { if (ch >= 0x20 && ch <= 0x7e) { /* characters in US-ASCII range converted directly */ string s; s = ch; return s; } if (cset == CHAR_LATIN1) { /* Latin1 ranged passed directly through if in latin1 */ if (ch >= 0x80 && ch <= 0xff) { string s; s += ch; return s; } } if (cset == CHAR_ASCII) { /* first lets try converting through dec */ if (flags & DEC && dec) { for (size_t i=0;i<ARRAY_SIZE(whats);i++) { if (whats[i].uni == ch && whats[i].vt100) { *dec = 1; string s; s = whats[i].vt100; return s; } } } } if (cset == CHAR_UNICODE) { /* If in JustLength, keep strlen()==wcswidth()) */ if (flags & JUSTLENGTH) { switch (mk_wcwidth(ch)) { case 0: return ""; case 1: return "?"; case 2: return "??"; default: return "?"; } } /* otherwise return it in UTF-8 */ return unitoutf8(ch); } if (has_table(cset)) { /* If we have a table, make use of the mapping */ string b; b += uni2cp(ch, cset); return b; } /* we may be able to find this character in the dec table */ if (flags & DEC && dec) { for (size_t i=0;i<ARRAY_SIZE(whats);i++) { if (whats[i].uni == ch && whats[i].vt100) { *dec = 1; string s; s = whats[i].vt100; return s; } } } for (size_t i=0;i<ARRAY_SIZE(whats);i++) { if (whats[i].uni == ch) { if (whats[i].latin1 && has_table(cset)) { string b; b += uni2cp(ch, cset); return b; } if (whats[i].ascii) { string s; s = whats[i].ascii; return s; } } } /* Give up */ switch (mk_wcwidth(ch)) { case 0: return ""; case 1: return "?"; case 2: return "??"; default: return "?"; } } //! the next colour to use for tripping static int next_tripcol(int a) { if (a==1) return 3; // red -> yellow if (a==3) return 2; // yellow -> green if (a==2) return 6; // green -> cyan if (a==6) return 4; // cyan -> blue if (a==4) return 5; // blue -> magenta if (a==5) return 1; // magenta -> red return 1; } //! the next colour to use of tripping in 256-colour mode static int next_tripcol256(int a) { static unsigned char tcols[] = { 226, 220, 214, 208, 202, 196, 197, 198, 199, 200, 201, 165, 129, 93, 57, 21, 27, 33, 39, 45, 51, 50, 49, 48, 47, 46, 82, 118, 154, 190, 226, 0 }; if (a==0) return tcols[0]; int i = 0; while (tcols[i]) { if (tcols[i]==a) return tcols[i+1]; i++; } return tcols[0]; } //! the colour parser! static string colour_parse(const char *src, colstate_t ¤t, colstate_t &tempblank, int flags, charset cset, const Player *who=NULL, const char *scheme=NULL) { colstate_t needed = current; string buf; int tripmode = 1; int tripping = who?who->get_int("$trip")>1:0; int in_tag = 0; string fn = ""; int cchanged = 0; while (*src) { string actual; needed.dec = 0; if (src[0]=='^') { int lock = 0; src++; if (!src[0]) { continue; } if (*src=='^') { goto end; } if (*src=='n') { if (!tripping) needed = tempblank; src++; continue; } if (*src=='}') { if (!tripping) needed = tempblank = blank; src++; continue; } if (!*src) { continue; } if (*src=='{') { lock = 1; src++; } if (!setfgcol(needed, *src, who, flags, scheme, tripping)) { if (isdigit(src[0]) && isdigit(src[1]) && isdigit(src[2])) if (flags & HAS_256COLS) { int rd = src[0]-'0'; int g = src[1]-'0'; int b = src[2]-'0'; if (rd>5) rd = 5; if (g>5) g = 5; if (b>5) b = 5; src += 3; if (!tripping) { needed.fgcol = 16 + (rd * 36 + g * 6 + b); needed.bold = isupper(*src); } cchanged = 1; } else { src += 3; char *wh = strchr(bcols, tolower(*src)); if (!tripping) { needed.fgcol = wh - bcols; needed.bold = isupper(*src); } cchanged = 1; } } else { cchanged = 1; } if (lock) tempblank = needed; if (*src=='#') { const char *anchor = src+1; if (!*anchor) { continue; } src++; while ((*src=='x' || isxdigit(*src))) src++; if (!*src) { continue; } if (*src==';') { src++; int i = atoi(anchor); if (*anchor=='x') { i = strtol(anchor+1, 0, 16); } if (i >= 0x20 && i <= 0x7f) { char tmp[2]; sprintf(tmp, "%c", i); actual = tmp; } else { actual = downgrade(i, flags, cset, &needed.dec); } src--; goto end; } } if (!*src) continue; for (size_t i=0;i<ARRAY_SIZE(whats);i++) { if (whats[i].code == *src) { if (cset == CHAR_UNICODE) { actual = downgrade(whats[i].uni, flags, cset); /* first use the actual unicode value (in UTF-8) */ } else if (flags & DEC && whats[i].vt100) { /* if we can do dec, and it is a dec character, then use that */ needed.dec = 1; actual = whats[i].vt100; } else if (cset == CHAR_ASCII) /* if our charset is ASCII, just use the ascii value */ actual = whats[i].ascii; else if (has_table(cset)) { /* if we have a table, try converting to unicode value to it */ actual = downgrade(whats[i].uni, flags, cset); if (actual == "?" || actual == "??") /* if that didn't work, try converting the latin1 value to it */ actual = downgrade((unsigned char)(*whats[i].latin1), flags, cset); } else /* just use the latin1 value */ actual = downgrade((unsigned char)(*whats[i].latin1), flags, cset); src--; break; } } src++; if (!actual.length()) continue; else goto end; } if (src[0]=='&') { src++; if (!src[0]) { continue; } if (src[0]=='/') { needed.ital = !needed.ital; src++; continue; } if (src[0]=='_') { needed.undl = !needed.undl; src++; continue; } if (src[0]=='|') { needed.sout = !needed.sout; src++; continue; } if (src[0]=='-') { src++; if (isdigit(src[0]) && isdigit(src[1]) && isdigit(src[2]) && src[3]) { int rd = src[0]-'0' <? 5; int gr = src[1]-'0' <? 5; int bl = src[2]-'0' <? 5; if (!tripping) { if (flags & HAS_256COLS) needed.bgcol = 16 + (rd * 36 + gr * 6 + bl); else if (char *wh = strchr(bcols, tolower(src[3]))) needed.bgcol = wh - bcols; } src += 4; continue; } else { char *wh = strchr(bcols, tolower(*src)); if (wh && !tripping) { needed.bgcol = wh - bcols; } src++; continue; } } if (src[0]=='=') { src++; if (!src[0]) { continue; } char *wh = strchr(bcols, tolower(*src)); if (wh && !tripping) { needed.fgcol = wh - bcols; needed.bold = tolower(*src); } src++; if (!src[0]) { continue; } wh = strchr(bcols, tolower(*src)); if (wh && !tripping) { needed.bgcol = wh - bcols; } src++; if (!src[0]) { continue; } continue; } if (src[0] == '&') goto end; src--; } if (*src=='\n' || *src=='\v') { if (*src=='\n') if (!tripping) needed = blank; if (flags & CRLF) actual = "\r\n"; } if (*src & 0x80) { actual = downgrade((unsigned char)*src, flags, cset); } if (*src == '\033') { tripmode = 0; } end: if (!in_tag && tripmode && tripping && (cchanged || random_number(2)==0)) { if (flags & HAS_256COLS) { needed.fgcol = next_tripcol256(needed.fgcol); } else { needed.fgcol = next_tripcol(needed.fgcol); if (needed.fgcol>=8) needed.fgcol = 1; } needed.bold = 1; cchanged = 0; } if (*src == '\2') { tripmode = 1; src++; continue; } string x; if (actual.length()) x = actual; else x = *src; int antidec = 0; const char *s = x.c_str(); while (*s) { if (*s != '\r' || *s != '\n') { // It's ok to send \r and \n in DEC mode antidec = 1; break; } s++; } if (!antidec) needed.dec = current.dec; if (!in_tag) { if (!(flags & NO_ANSI)) { if (who && who->p && who->p->mxp) if (x[0]!=3) buf += make_mxpdifference(current, needed); else // forcibly reset MXP codes before a <> buf += make_mxpdifference(current, blank); else buf += make_difference(current, needed); } else if (!(flags & JUSTLENGTH)) buf += make_decdifference(current, needed); } if (who && who->p && who->p->mxp) { const char *xs = x.c_str(); while (*xs) { char c = *xs; xs++; switch (c) { case 3: buf += "<"; in_tag++; break; case 4: buf += ">"; in_tag--; break; case '\n': buf += who->p->secure_locked?"\n":"\n\033[1z"; break; default: if (in_tag < 1) switch (c) { case '&': buf += "&"; break; case '<': buf += "<"; break; case '>': buf += ">"; break; default: buf += c; } else buf += c; } } } else { const char *xs = x.c_str(); while (*xs) { if (*xs=='\3') in_tag++; if (!in_tag) buf += *xs; if (*xs=='\4') in_tag--; xs++; } } src++; } if (!(flags & NO_ANSI)) buf += make_difference(current, needed); return buf; } //! wrapper round the parser static string msi_colour_parse(colstate_t *colstate, const char *what, colourinfo_t cu) { colstate_t defcolstate = blank; colstate_t tblank = blank; int flags = CRLF; if (!cu.colour) { flags |= NO_ANSI; } if (cu.dec==2) /* lines on */ flags |= DEC; if (cu.dec==1) /* lines auto */ { if (streq(cu.term, "xterm") || streq(cu.term, "screen") || streq(cu.term, "vt100") || streq(cu.term, "vt102") || streq(cu.term, "vt320") || streq(cu.term, "cryotel") || streq(cu.term, "dtterm") || streq(cu.term, "ucryotel")) flags |= DEC; } if (cu.xtermcols==256) flags |= HAS_256COLS; if (cu.who && cu.who->p && cu.who->p->mxp) flags |= HAS_256COLS; if (cu.cset==CHAR_FAKEUNI) { cu.cset = CHAR_UNICODE; flags |= JUSTLENGTH; } string res = ""; res += colour_parse(what, colstate?*colstate:defcolstate, tblank, flags, cu.cset, cu.who, cu.scheme); if (!colstate) { res += colour_parse("^n", defcolstate, tblank, flags, cu.cset, cu.who, cu.scheme); /* is this ever called with p==0 and cu.colour == 1? */ } return res; } //! wrapper round the parser to get the lenght static size_t get_length(const char *a, int lenof, const colourinfo_t &ci) { colstate_t colstate = blank; colstate_t tblank = blank; string res; int flags = 0; if (ci.dec==2) /* lines on */ flags |= DEC; if (ci.dec==1) /* lines auto */ { if (streq(ci.term, "xterm") || streq(ci.term, "screen") || streq(ci.term, "vt100") || streq(ci.term, "vt102") || streq(ci.term, "vt320") || streq(ci.term, "cryotel") || streq(ci.term, "dtterm") || streq(ci.term, "ucryotel")) flags |= DEC; } if (lenof != -1) { string tmp = string(a, lenof); res = colour_parse(tmp.c_str(), colstate, tblank, flags|NO_ANSI|JUSTLENGTH, ci.cset); } else { res = colour_parse(a, colstate, tblank, flags|NO_ANSI|JUSTLENGTH, ci.cset); } return res.length(); } //! custom colour struct custom_colour { const char *n; const char *ui; const char *def; PFlag priv; int minlev; } cols[] = { { "L", "darkgray",}, { NULL, NULL }, { "o", "object" }, { "p", "player" }, { "P", "mobile" }, { "d", "direction" }, { "D", "destination" }, { NULL, NULL }, { "s", "say" }, { "t", "tell" }, { "k", "whisper" }, { NULL, NULL }, { "i", "shout" }, { "S", "chat", 0, PFL_NONE, 4} , { "col.com", "hero", "^B", PFL_NONE, LEV_COMMANDER }, { "col.cap", "esteemed", "^m", PFL_NONE, LEV_CAPTAIN }, { "col.adm", "revered", "^Y", PFL_NONE, LEV_ADMIRAL }, { NULL, NULL }, { "col.achat", "achat", "^c", PFL_ABERCHAT }, { "col.acode", "acode", "^y", PFL_CODER }, { "col.agod", "agod", "^R", PFL_GOD }, }; //! show the custom colours of you or another static bool verb_showcol(MudObject *who, int argc, const char **argv) { MudObject *qui = who; if (argc>1 && who->get_priv(PFL_SEEINFO)) { qui = get_player(who, argv[1]); if (!qui) { who->printf("View colour scheme of who?\n"); return true; } } who->printf("%s\n\n", title_for(qui==who?"Showcol":ssprintf("Showcol for %M", qui).c_str(), who).c_str()); const char *def = qui->get("colours"); if (!def) def = "classic"; who->printf(" Colours based on the '%#s' scheme.\n\n", def); for (size_t i=0;i<ARRAY_SIZE(cols);i++) { const char *n = cols[i].n; if (!n) { who->printf("\n"); continue; } if (privs_of(qui) < cols[i].minlev) continue; if (cols[i].priv != PFL_NONE && !qui->get_priv(cols[i].priv)) continue; const char *s = qui->get(ssprintf("col.%s", n).c_str()); if (!s) { s = qui->get(n); } if (strlen(n)>1) { if (s) who->printf(" %s%15s^n ^W^%s^n", s, cols[i].ui, s); else who->printf(" %s%15s^n ^%s", cols[i].def, cols[i].ui, cols[i].def); } else { lookup_t a = lookup_classic; if (!strcmp(def, "yellow")) a = lookup_yellow; if (!strcmp(def, "red")) a = lookup_red; if (!strcmp(def, "paper")) a = lookup_paper; char l = a(*n); if (s) { who->printf(" %s%15s^n ^W^%s^n", s, cols[i].ui, s?s:""); } else { who->printf(" ^%c%15s^n ^^%c", l, cols[i].ui, l); } } who->printf(" %s\n", cols[i].ui); } who->printf("\n"); who->printf("%s\n", footer_for(who).c_str()); return true; } //! set a custom colour static bool verb_setcol(MudObject *who, int argc, const char **argv) { if (argc < 2) return 1; for (size_t i=0;i<ARRAY_SIZE(cols);i++) { const char *n = cols[i].ui; char prop[100]; if (!cols[i].n) continue; if (strlen(cols[i].n)==1) sprintf(prop, "col.%c", *cols[i].n); else sprintf(prop, "%s", cols[i].n); if (n && streq(n, argv[1])) { if (argc < 3) { who->printf("Removing %s.\n", prop); who->unset(prop); } else { who->printf("Setting colour for %s to %s.\n", argv[1], argv[2]+1); who->set(prop, argv[2]); } return true; } } who->printf("'%s' isn't a customisable colour.\n", argv[1]); return true; } //! monochrome static bool verb_mono(MudObject *p, int, const char **) { if (!p->get_flag(FL_COLOUR)) { p->printf("Colour already off.\n"); return true; } p->printf("\033[0m\2Colour turned off.\n"); p->set_bflag(FL_COLOUR, 0); return true; } //! set a colour scheme static bool verb_colour(MudObject *p, int argc, const char **argv) { if (argc>1) { if (streq(argv[1], "mono") || streq(argv[1], "off")) { return verb_mono(p, 0, 0); } if (streq(argv[1], "paper")) { p->set_bflag(FL_COLOUR, 1); p->set("colours", "paper"); p->printf("Colour scheme ^WPaper^n selected.\n"); return true; } if (streq(argv[1], "yellow")) { p->set_bflag(FL_COLOUR, 1); p->set("colours", "yellow"); p->printf("Colour scheme ^YYellow^n selected.\n"); return true; } if (streq(argv[1], "red")) { p->set_bflag(FL_COLOUR, 1); p->set("colours", "red"); p->printf("Colour scheme ^RRed^n selected.\n"); return true; } if (streq(argv[1], "classic")) { p->set_bflag(FL_COLOUR, 1); p->set("colours", "classic"); p->printf("Colour scheme ^rclassic^n selected.\n"); return true; } p->printf("Known colour schemes : classic mono yellow paper.\n"); return true; } if (p->get_flag(FL_COLOUR)) { p->set_bflag(FL_COLOUR, 1); p->printf("Known colour schemes : classic mono yellow paper.\n"); return true; } else { p->set_bflag(FL_COLOUR, 1); p->printf("Welcome to the wonderful world of ^RC^YO^GL^CO^BU^MR^R!^n\n"); } return true; } #define CLEANUP \ msi_colourfilter = 0;\ msi_stringlength = 0; #include "verbmodule.h" void startup() { msi_colourfilter = msi_colour_parse; msi_stringlength = get_length; AUTO_VERB(showcol, 5, 0, PFL_NONE); AUTO_VERB(setcol, 5, 0, PFL_NONE); AUTO_VERB(graph, 5, 0, PFL_NONE); AUTO_VERB(colour, 5, 0, PFL_NONE); ADD_ALIAS(color, 4, colour); AUTO_VERB(mono, 4, 0, PFL_NONE); }