ID:195072
 
//Title: Text-Mode Terminal Colours
//Credit to: Jtgibson
//Contributed by: Jtgibson


/*
This system allows you to store colours in the form of single numbers from
0-255, and then converts those numbers into HTML font strings which you can use
for BYOND's text-mode map display.

For instance, say I have a mob:

mob/player

That mob has a "symbol" variable...

symbol = "@"

I also have a "colour" variable for that mob...

colour = DARKRED

When the mob is created, I just call SetColour(src.colour)...

New()
..()
SetColour(src.colour)

...and ba-da-boom, it works as follows:

src.text = "<font color=#A00>@"

And in Dream Seeker in text mode, the mob will be a red @ symbol!


NOTE: A terminal is designed not to render characters that are pure black on
black. If you pass the colour 0x00 to the colourHTML() proc, it will be
treated exactly as if you used the colour DARKWHITE (0x07).

NOTE: The 'text' variables after having been set via this snippet are not
intended to be output to the text window and will appear unusual if their
blinking bit is set. You can, however, temporarily build a temporary string of
the format:

"[colourHTML(colour&NOBLINK_MASK)][src.symbol]</font>"

which will output properly to the text window.


NOTE: You may use American spellings: SetColor(), SetForegroundColor(), etc.
The variable 'colour', however, cannot be aliased to anything else, so you'll
have to use the 'colour' variable directly if you want to adjust it directly.
You can define a 'color' variable and use SetColor(src.color) in New(), but
you'll have to remember to check 'colour' and not 'color' when checking the
current state of the atom's colour.


********************************************************************************

A Terminal Colour Primer

Terminal colours consist of eight bits:

0 0 0 0 1 1 0 0

| | | | | | | `- Foreground Green
| | | | | | `- Foreground Blue
| | | | | `- Foreground Red
| | | | `- Bright Bit
| | | `- Background Green
| | `- Background Blue
| `- Background Red
`- Blinking Bit


Foreground colours apply to the symbol itself. For instance, within this
comment, the following symbol:

@

has a foreground colour of "grey" (or "gray" for American speakers).


Background colours apply to the space immediately behind the symbol. Using the
same example:

@

the background colour is "white".


In ANSI, the foreground colour "grey" is called "dark white" and is represented
in binary as "0b00000111" (0x07). In standard mode 0, CGA colour, this library
will represent "dark white" as the HTML colour "#AAA". In mode 1, W3C colour,
the library will instead represent it as the HTML colour "C0C0C0".


The "bright bit" goes from a standard "dim" looking colour to a brighter one.
In the case of red, for instance, it goes from the HTML colour "#A00" for
DARKRED (in CGA colour) to the HTML colour "#F55" for BRIGHTRED, which is much
brighter! These colours in W3C colour are "#800000" and "FF0000".


The "blink bit" causes the symbol to blink out three times every two seconds.
It can be utilised only via the atom/SetColour() function provided herein, as
there is no adequate way of expressing it via the colourHTML proc, which is
concerned only with producing the foreground and background colours and their
brightnesses.

*/



//// Internal definitions. Don't use these flags directly. /////
var/const
COLOUR_BLACK = 0x0
COLOUR_BLUE = 0x1
COLOUR_GREEN = 0x2
COLOUR_CYAN = 0x3 // = COLOUR_GREEN|COLOUR_BLUE
COLOUR_RED = 0x4
COLOUR_MAGENTA = 0x5 // = COLOUR_RED|COLOUR_BLUE
COLOUR_YELLOW = 0x6 // = COLOUR_RED|COLOUR_GREEN
COLOUR_WHITE = 0x7 // = COLOUR_RED|COLOUR_GREEN|COLOUR_BLUE

COLOUR_BRIGHT = 0x8

COLOUR_BLINK = COLOUR_BRIGHT << 4

FOREGROUND_MASK = 0x0F
BACKGROUND_MASK = 0xF0
NOBLINK_MASK = 0x7F


///// Use THESE colours when defining your objects /////
var/const
//DARK FOREGROUND COLOURS:
DARKBLACK = COLOUR_BLACK
DARKBLUE = COLOUR_BLUE
DARKGREEN = COLOUR_GREEN
DARKCYAN = COLOUR_CYAN
DARKRED = COLOUR_RED
DARKMAGENTA = COLOUR_MAGENTA
DARKYELLOW = COLOUR_YELLOW
DARKWHITE = COLOUR_WHITE

//BRIGHT FOREGROUND COLOURS:
BRIGHTBLACK = COLOUR_BLACK | COLOUR_BRIGHT
BRIGHTBLUE = COLOUR_BLUE | COLOUR_BRIGHT
BRIGHTGREEN = COLOUR_GREEN | COLOUR_BRIGHT
BRIGHTCYAN = COLOUR_CYAN | COLOUR_BRIGHT
BRIGHTRED = COLOUR_RED | COLOUR_BRIGHT
BRIGHTMAGENTA = COLOUR_MAGENTA | COLOUR_BRIGHT
BRIGHTYELLOW = COLOUR_YELLOW | COLOUR_BRIGHT
BRIGHTWHITE = COLOUR_WHITE | COLOUR_BRIGHT

//BACKGROUND COLOURS: (always dim)
BACKGROUND_BLACK = COLOUR_BLACK << 4
BACKGROUND_BLUE = COLOUR_BLUE << 4
BACKGROUND_GREEN = COLOUR_GREEN << 4
BACKGROUND_CYAN = COLOUR_CYAN << 4
BACKGROUND_RED = COLOUR_RED << 4
BACKGROUND_MAGENTA = COLOUR_MAGENTA << 4
BACKGROUND_YELLOW = COLOUR_YELLOW << 4
BACKGROUND_WHITE = COLOUR_WHITE << 4

//To set an object to be dark grey on a red background, use:
// BRIGHTBLACK | BACKGROUND_RED


proc/colourHTML(colour=COLOUR_WHITE, mode=0)
var/colourHTML = ""
var/bgcolourHTML = ""

if(colour & BACKGROUND_MASK)
switch((colour & BACKGROUND_MASK))
if(BACKGROUND_WHITE)
if(mode == 1) bgcolourHTML = "C0C0C0"
else bgcolourHTML = "AAA"
else
if(mode == 1)
bgcolourHTML = "\
[colour & BACKGROUND_RED ? "80":"00"]\
[colour & BACKGROUND_GREEN ? "80":"00"]\
[colour & BACKGROUND_BLUE ? "80":"00"]"
else
bgcolourHTML = "\
[colour & BACKGROUND_RED ? "A":"0"]\
[colour & BACKGROUND_GREEN ? "A":"0"]\
[colour & BACKGROUND_BLUE ? "A":"0"]"

if(!colour)
if(mode == 1) colourHTML = "C0C0C0"
else colourHTML = "AAA"
else
switch(colour & FOREGROUND_MASK)
if(DARKWHITE)
if(mode == 1) colourHTML = "C0C0C0"
else colourHTML = "AAA"
if(BRIGHTBLACK)
if(mode == 1) colourHTML = "808080"
else colourHTML = "555"
if(DARKYELLOW)
if(mode == 1) colourHTML = "808000"
else colourHTML = "A50"
else
var/on
var/off = "00"
if(mode == 1)
on = "80"; if(colour & COLOUR_BRIGHT) on = "FF"
else
on = "A"
off = "0"
if(colour & COLOUR_BRIGHT)
on = "F"
off = "5"
colourHTML = "\
[colour & COLOUR_RED ? on : off]\
[colour & COLOUR_GREEN ? on : off]\
[colour & COLOUR_BLUE ? on : off]"

if(bgcolourHTML)
return "<font color=#[colourHTML] bgcolor=#[bgcolourHTML]>"
return "<font color=#[colourHTML]>"


atom
var/colour = DARKWHITE|BACKGROUND_BLACK
var/symbol = ""

proc/GetColour() return colour
proc/GetBackgroundColour() return colour & BACKGROUND_MASK
proc/GetForegroundColour() return colour & FOREGROUND_MASK
proc/GetSymbol() return symbol


//Takes a full colour code from 0-255 and sets src.text according to
// the new colour.
proc/SetColour(colour)
src.colour = colour

var/symbol = GetSymbol()
if(!symbol) symbol = initial(text)

if(colour & COLOUR_BLINK)
//Simulate blinking effect with three spaces and three symbols.
//Caveat: does not support pre-existing animations (i.e., where
// your symbol is more than 1 character long). I could write up
// a routine which does so, but it'd vastly complicate things.
text = "[colourHTML(colour)] [symbol][symbol][symbol]"
else
text = "[colourHTML(colour)][symbol]"


//Takes a BACKGROUND_### colour (e.g., BACKGROUND_WHITE, etc.)
proc/SetBackgroundColour(colour)
var/background_colour = colour & BACKGROUND_MASK
SetColour((src.colour & FOREGROUND_MASK) | background_colour)


//Takes a DARK### or BRIGHT### colour (e.g., DARKRED, BRIGHTWHITE, etc.)
proc/SetForegroundColour(colour)
var/foreground_colour = colour & FOREGROUND_MASK
SetColour((src.colour & BACKGROUND_MASK) | foreground_colour)


proc/SetSymbol(symbol)
src.symbol = symbol
SetColour(GetColour()) //reupdate src.text variable



///// American strings /////

var/const
COLOR_BLACK = COLOUR_BLACK
COLOR_BLUE = COLOUR_BLUE
COLOR_GREEN = COLOUR_GREEN
COLOR_CYAN = COLOUR_CYAN
COLOR_RED = COLOUR_RED
COLOR_MAGENTA = COLOUR_MAGENTA
COLOR_YELLOW = COLOUR_YELLOW
COLOR_WHITE = COLOUR_WHITE

COLOR_BRIGHT = COLOUR_BRIGHT
COLOR_BLINK = COLOUR_BLINK

atom/proc/GetColor() return colour
atom/proc/SetColor(color) return SetColour(color)
atom/proc/GetBackgroundColor() return colour & BACKGROUND_MASK
atom/proc/SetBackgroundColor(color) return SetBackgroundColour(color)
atom/proc/GetForegroundColor() return colour & FOREGROUND_MASK
atom/proc/SetForegroundColor(color) return SetForegroundColour(color)

proc/colorHTML(color=COLOUR_WHITE) return colourHTML(color)



///*
//Testing code/sample implementation:

mob/verb/test_colourHTML()
var/list/colours = list(
"DARKBLACK"=DARKBLACK,
"DARKBLUE"=DARKBLUE,
"DARKGREEN"=DARKGREEN,
"DARKCYAN"=DARKCYAN,
"DARKRED"=DARKRED,
"DARKMAGENTA"=DARKMAGENTA,
"DARKYELLOW"=DARKYELLOW,
"DARKWHITE"=DARKWHITE,
"BRIGHTBLACK"=BRIGHTBLACK,
"BRIGHTBLUE"=BRIGHTBLUE,
"BRIGHTGREEN"=BRIGHTGREEN,
"BRIGHTCYAN"=BRIGHTCYAN,
"BRIGHTRED"=BRIGHTRED,
"BRIGHTMAGENTA"=BRIGHTMAGENTA,
"BRIGHTYELLOW"=BRIGHTYELLOW,
"BRIGHTWHITE"=BRIGHTWHITE)

usr << "Mode 0: CGA"
for(var/colour in colours)
usr << colourHTML(colours[colour]) + colour + "</font> " + \
html_encode(colourHTML(colours[colour]))
usr << null //blank line

usr << "Mode 1: W3C"
for(var/colour in colours)
usr << colourHTML(colours[colour], mode=1) + colour + "</font> " + \
html_encode(colourHTML(colours[colour], mode=1))
usr << null //blank line

usr << "Note: DARKBLACK in both modes will appear as DARKWHITE because it \
is assumed to be drawn on a black background, and the library intentionally \
does not allow black-on-black to be rendered."


//*/
Awesome this is really nifty :)